Portal for car enthusiasts

AC voltmeter on arduino. bluetooth voltmeter based on arduino

Are there times when you want to check voltage or some point in a circuit, but don't have a voltmeter or multimeter handy? Run to buy? It's long and expensive. Before you do that, how about making your own voltmeter? In fact, with simple components, you can make it yourself.

Step 1: Preparing the Ingredients

  • In the tutorial, we used an Arduino compatible board - SunFounder Uno / Mars (http://bit.ly/2tkaMba)
  • USB data cable
  • 2 potentiometers (50k)
  • LCD1602 - http://bit.ly/2ubNEfi
  • Breadboard - http://bit.ly/2slvfrB
  • Multiple jumpers

Before connecting, let's first understand how it works.

Use the SunFounder Uno board for the main part of the voltmeter data processing, LCD1602 as the screen, a potentiometer to adjust the LCD contrast, and the other to divide the voltage.

When you turn a potentiometer connected to the Uno board, the potentiometer's resistor changes, thereby changing the voltage across it. The voltage signal will be sent to the Uno board through the A0 pin, and the Uno will convert the received analog signal to digital form and write to the LCD. So you can see the voltage value at the current capacitance resistance.

The LCD1602 has two modes of operation: 4-bit and 8-bit. When the IO of the MCU is insufficient, you can choose 4-bit mode, which only uses pins D4 ~ D7.

Follow the table to connect them.

Step 4: Connect Potentiometer to LCD1602

Connect the middle pin of the potentiometer to the Vo pin on the LCD1602 and any of the other pins to GND.

Connect the middle pin of the potentiometer to pin A0 of the SunFounder Uno and one of the others to 5V while the other is to GND.

Step 6: Uploading the Code

Code like this:

#include /*******************************************************/ const int analogIn = A0;//potentiometer attach to A0 LiquidCrystal lcd(4, 6, 10, 11, 12, 13);//lcd(RS,E,D4,D5,D6.D7) float val = 0;// define the variable as value=0 /****************************************************/ void setup() ( Serial. begin(9600);//Initialize the serial lcd.begin(16, 2);// set the position of the characters on the LCD as Line 2, Column 16 lcd.print("Voltage Value:");//print "Voltage Value:" ) /*********************************************************/ void loop() ( val = analogRead(A0);//Read the value of the potentiometer to val val = val/1024*5.0; // Convert the data to the corresponding voltage value in a math way Serial.print(val);//Print the number of val on the serial monitor Serial.print("V"); // print the unit as V, short for voltage on the serial monitor lcd.setCursor(6,1);//Place the cursor at Line 1, Column 6. From here the characters are to be displayed lcd.print(val);//Print the number of val on the LCD lcd.print("V");//Then print the unit as V, short for voltage on the LCD delay(200); //Wait for 200ms )

Rotate the potentiometer to check the voltage on the LCD1602 in real time.

Here's a tricky one. After I ran the code, the LCD showed symbols. I then adjusted the screen contrast (gradual change from black to white) by turning the potentiometer clockwise or counterclockwise until the screen displayed the characters clearly.

Take two batteries to measure their voltage: 1.5V and 3.7V. Unhook the connection of the second potentiometer to pin A0 and GND, which means removing the potentiometer from the circuit. Clamp the end of wire A0 to the anode of the battery and the GND circuit to the cathode. DO NOT plug them back in or you will short circuit the battery. The value 0V is the reverse connection.

So, the battery voltage is displayed on the LCD. There may be some error between the value and the nominal because the battery is not fully charged. And that's why I need to measure the voltage to understand if I can use the battery or not.

PS: If you have display problems, see this FAQ for LCDs - http://wiki.sunfounder.cc/index.php?title=LCD1602/I2C_LCD1602_FAQ.

Idea

Idea devices for measuring voltage, current, capacitance, discharge, and maybe charge arose a long time ago and not only with me. You can find quite a few toys called USB Tester (Doctor) for testing various USB devices. I'm interested in a somewhat more versatile device, independent of the interface, but simply designed for certain voltages and currents. For example, 0 - 20.00v, 0 - 5.00a, 0 - 99.99Ah. As for functions, I see it like this

  • Display of current voltage and current, that is, a volt-ampere meter. In principle, you can immediately reflect the power.
  • Calculation and display of the accumulated capacity. In amp-hours and most likely in watt-hours.
  • Process time display
  • And, most likely, configurable lower and upper voltage shutdown thresholds (discharge and charge limits)

Development

To implement calculations and measurements, we need a controller. I remembered this idea as part of my acquaintance with Arduino, so the simple popular Atmega328 will be the controller and it will be programmed in the environment Arduino. From an engineering point of view, the choice is probably not the best - the controller for the task is a little fat, and its ADC cannot be called measuring, but ... we will try.

  • We will not solder much in this project. As a basis, we will take the ready-made Arduino Pro Mini module, since the Chinese are ready to supply them for $ 1.5 at retail.
  • The display device will be a 1602 display - $1.5 more. I have an option with an I2C interface module, but in this project it is not much needed ($0.7).
  • For development, we need a breadboard. In my case, this is a small $1 BreadBoard.
  • Of course, you will need wires and a number of resistors of different ratings. For a 1602 display without I2C, contrast selection is also needed - it is done with a variable resistor of 2 - 20 kOhm.
  • To implement an ammeter, you need a shunt. As a first approximation, it can be a 0.1 ohm, 5 watt resistor.
  • To implement automatic shutdown, you need a relay with contacts designed for the maximum current of the device and a voltage equal to the supply voltage. To control the relay, you need an npn transistor and a protective diode.
  • The device will be powered by an external power supply, obviously at least 5V. If the power supply varies greatly, then an integral stabilizer of type 7805 will also be required - it will determine the voltage of the relay.
  • When Arduino Pro Mini will require a USB-TTL converter to flash the firmware.
  • You will need a multimeter to set it up.

Voltmeter

I am implementing a simple voltmeter with one range of about 0 - 20v. This remark is important, since the ADC of our controller has a capacity of 10 bits (1024 discrete values), so the error will be at least 0.02 V (20 / 1024). To implement the hardware, we need an analog input of the controller, a divider from a pair of resistors and some kind of output (the display is in a finished version, a serial port can be used for debugging).

The principle of ADC measurement is to compare the voltage at the analog input with the reference VRef. The ADC output is always integer - 0 corresponds to 0V, 1023 corresponds to VRef voltage. The measurement is implemented by a series of successive readings of the voltage and averaging over the period between updates of the value on the screen. The choice of reference voltage is important because it defaults to the supply voltage, which may not be stable. This does not suit us at all - we will take as a basis a stable internal reference source with a voltage of 1.1V, initializing it with a call to analogReference(INTERNAL). Then we will calibrate its value according to the readings of the multimeter.

In the diagram on the left - a variant with direct control of the display (it is simply controlled - see the standard LiquidCrystal\HelloWorld sketch). On the right is the I2C option, which I will use further. I2C allows you to save on wires (which in the usual version are 10, not counting the backlight). But this requires an additional module and more complex initialization. In any case, the display of symbols on the module must first be checked and the contrast adjusted - for this, you just need to display any text after initialization. The contrast is adjusted by resistor R1, or a similar resistor of the I2C module.

The input is a 1:19 divider, which allows, at Vref = 1.1, to obtain a maximum voltage of about 20V (usually a capacitor + a zener diode is placed in parallel with the input for protection, but this is not important for us yet). Resistors have a spread, and the reference Vref of the controller too, so after assembly, you need to measure the voltage (at least power supply) in parallel with our device and a reference multimeter and select Vref in the code until the readings match. It is also worth noting that any ADC has a zero offset voltage (which spoils the readings at the beginning of the range), but we will not delve into this yet.

It will also be important to separate the supply and measuring "ground". Our ADC has a resolution slightly worse than 1mV, which can be problematic if wired incorrectly, especially on a breadboard. Since the wiring of the module board has already been done and we are left with a choice of pins. The module has several "ground" pins, so we must make sure that the power supply to the module comes through one "ground" and measurements through another. In fact, I always use the ground pin closest to the analog inputs for changes.

For I2C control, a variant of the LiquidCrystal_I2C library is used - in my case, a specific pinout of the I2C module is indicated (the Chinese produce modules with different controls). I also note that I2C in Arduino involves the use of pins A4, A5 - on the Pro Mini board they are not on the edge, which is inconvenient for prototyping on BreadBoard.

Source

#include #include // Simple voltmeter with i2c display 1602. V 16.11 // Settings for i2c display 1602 with custom pinout #define LCD_I2C_ADDR 0x27 #define BACKLIGHT 3 #define LCD_EN 2 #define LCD_RW 1 #define LCD_RS 0 #define LCD_D4 4 #define LCD_D5 5 #define LCD_D6 6 #define LCD_D7 7 LiquidCrystal_I2C lcd(LCD_I2C_ADDR,LCD_EN,LCD_RW,LCD_RS,LCD_D4,LCD_D5,LCD_D6,LCD_D7); // Refresh time, ms (200-2000) #define REFRESH_TIME 330 // Analog input #define PIN_VOLT A0 // Internal reference voltage (select) const float VRef = 1.10; // Input resistive divider ratio (Rh + Rl) / Rl. IN<-[ Rh ]--(analogInPin)--[ Rl ]--|GND const float VoltMult = (180.0 + 10.0) / 10.0; float InVolt, Volt; void setup() { analogReference(INTERNAL); // Инициализация дисплея lcd.begin (16, 2); lcd.setBacklightPin(BACKLIGHT, POSITIVE); lcd.setBacklight(HIGH); // включить подсветку lcd.clear(); // очистить дисплей lcd.print("Voltage"); } void loop() { unsigned long CalcStart = millis(); int ReadCnt = 0; InVolt = 0; // Чтение из порта с усреднением while ((millis() - CalcStart) < REFRESH_TIME) { InVolt += analogRead(PIN_VOLT); ReadCnt++; } InVolt = InVolt / ReadCnt; // Смещение 0 для конкретного ADC (подобрать или отключить) if (InVolt >0.2) InVolt += 3; // Convert to volts (Value: 0..1023 -> (0..VRef) scaled by Mult) Volt = InVolt * VoltMult * VRef / 1023; // Display data lcd.setCursor(0, 1); lcd.print(Volt); lcd.print("V"); )

Schematic diagram of a homemade bipolar voltmeter on the Arduino Uno and with a 1602A display. In the article “Double voltmeter on ARDUINO UNO” (L.1), the author proposed a description of a voltmeter and a program for the simultaneous measurement and indication of two constant voltages. Which is very convenient if you need to measure two constant voltages at the same time and compare them.

This may be required, for example, when repairing or setting up a DC voltage stabilizer in order to measure the voltage at its input and output, or in other cases.

However, there are circuits with bipolar power, when the voltage at some point in the circuit relative to the common "zero" can be either positive or negative.

circuit diagram

This describes how to modify the circuit and program so that the device can measure and indicate both positive and negative voltage.

To begin with, the measured voltages are fed to two analog inputs A1 and A2. There are six analog inputs in total, - A0-A5, you could choose any two of them. In this case, A1 and A2 are selected. The voltage on the analog ports can only be positive and only within the range from zero to the microcontroller supply voltage, that is, nominally up to 5V.

The output of the analog port is converted by the ADC of the microcontroller into digital form. To get the result in units of volts, you need to multiply it by 5 (by the reference voltage, that is, by the microcontroller supply voltage) and divide by 1024.

Rice. 1. Schematic diagram of a bipolar voltmeter on the Arduino Uno and 1602A.

In order to be able to measure a voltage of more than 5V, or rather, more than the supply voltage of the microcontroller, because the real voltage at the output of the 5-volt regulator on the ARDUINO UNO board may differ from 5V, and usually a little lower, you need to use ordinary resistive dividers at the input.

Here, these are voltage dividers across resistors R1, R3 and R2, R4. But what if the voltage needs to be measured less than zero? In this case, there is only one way out - it is to raise the input zero level. Ideally, you need half the supply voltage, that is, up to 2.5V. At the same time, 2.5V data will be added to the input voltage.

Then, programmatically, this voltage is simply subtracted from the measured one. But, this will require the need for an additional source of this voltage. In principle, this is not difficult to do, but there is an easier solution.

In addition to the 5V voltage regulator, the ARDUINO UNO board also has a 3.3V voltage source. Here it can be used as a "virtual zero" for entry.

Changes in the circuit are visible in Figure 1. Compared to the first option, the input "zero" is simply rearranged from a common zero to a +3.3V source. Therefore, when the input voltage is positive, it is more than 3.3V at the input (but not more than 5V - this is the upper limit of measurement), and when it is negative - less than 3.3V (but not less than OV - this is the lower limit of measurement).

The increase in the measurement limits (modulo) is achieved by a resistive divider, and the indication of the actual input voltage supplied to X2 and X3 by software subtraction of the value of 3.3V from the voltage at the microcontroller inputs.

The program is shown in Table 1. This can be seen in the lines:

volt=(vout*5.0/1024.0-3.3)/0.048 ;

voltl=(voutl*5.0/1024.0-3.3)/0.048;

The number 3.3 is just the given voltage of the “virtual zero” of the input.

In these lines, the number 5.0 is the voltage at the output of the ARDUINO UNO board stabilizer. Ideally, it should be 5V, but for the accurate operation of the voltmeter, this voltage must first be measured. Connect the power supply and measure the +5V voltage at the POWER connector of the board with a sufficiently accurate voltmeter.

Whatever happens, then enter in these lines instead of 5.0. The same applies to voltage + 3.3V - it must be measured on the board connector, because in fact it may differ slightly from 3.3V. For example, if "5V" is actually 4.85V and "3.3V" is actually 3.32V, the lines would look like this:

volt=(vout*4.85/1024.0-3.32)/0.048;

voltl=(voutl*4.85/1024.0-3.32)/0.048;

At the next stage, it will be necessary to measure the actual resistances of the resistors R1-R4 and determine the K coefficients (0.048 are indicated) for these lines using the formulas:

K1 = R3 / (R1+R3) and K2 = R4 / (R2+R4)

Let's say K1 = 0.046, and K2 = 0.051, so we write:

volt=(vout*4.85/1024.0-3.32)/0.046;

voltl=(voutl*4.85/1024.0-3.32)/0.051;

Thus, the text of the program needs to be amended according to the actual voltage at the output of the 5-volt and 3.3-volt stabilizers of the ARDUINO UNO board, and according to the actual division ratios of the resistive dividers.

After that, the device will work accurately, and it will not require any adjustment or calibration. When measuring a negative voltage on the LCD indicator in the corresponding line before the voltage value there will be a minus sign. When measuring positive voltage, there is no sign.

By changing the division coefficients of the resistive dividers (and, accordingly, the "K" coefficients), it is possible to make other measurement limits, and not necessarily the same for both inputs.

I want to remind you that a 1602A type H1 liquid crystal display module is connected to the digital ports D2-D7 of the ARDUINO UNO board. The LCD indicator is powered by a 5V voltage regulator, which is available on the 5V voltage regulator board.

In order for the indicator to interact with ARDUINO UNO, you need to load a subroutine into the program to control it. Such routines are called "libraries", and there are many different "libraries" in the software package for ARDUINO UNO. The HD44780 based LCD display requires the LiquidCrystal library. Therefore, the program (Table 1) starts by loading this library:

This line instructs to load this library into ARDUINO UNO. Next, you need to assign the ARDUINO UNO ports that will work with the LCD indicator. I have selected ports from D2 to D7. You can choose others. These ports are assigned by the string:

LiquidCrystal led(2, 3, 4, 5, 6, 7);

After that, the program proceeds to the actual operation of the voltmeter.

Karavkin V. RK-06-17.

Literature: 1. Karavkin V. - Double voltmeter on ARDUINO UNO. RK-01-17.

Analog inputs of the Arduino board.

The Arduino UNO board contains 6 analog inputs for measuring voltage signals. It would be more correct to say that 6 outputs of the board can operate in the mode of both discrete outputs and analog inputs.

These pins are numbered 14 to 19. They are initially set up as analog inputs and can be referred to as A0-A5. At any time, they can be configured to the mode of discrete outputs.

pinMode(A3, OUTPUT); // setting the discrete output mode for A3
digitalWrite(A3, LOW); // setting output A3 low

To return to analog input mode:

pinMode(A3, INPUT); // setting the analog input mode for A3

Analog inputs and pull-up resistors.

Pull-up resistors are connected to the analog input pins, as well as to the digital pins. The inclusion of these resistors is performed by the command

digitalWrite(A3, HIGH); // turn on the pull-up resistor to input A3

The command must be applied to an output configured in input mode.

It must be remembered that the resistor can affect the level of the input analog signal. The current from the 5V power supply, through the pull-up resistor, will cause a voltage drop across the internal resistance of the signal source. So it is better to turn off the resistor.

Analog-to-digital converter board Arduino.

The actual voltage measurement at the inputs is performed by an analog-to-digital converter (ADC) with a switch for 6 channels. The ADC has a resolution of 10 bits, which corresponds to the converter output code 0...1023. The measurement error is not more than 2 units of the least significant digit.

To maintain maximum accuracy (10 digits), it is necessary that the internal resistance of the signal source does not exceed 10 kΩ. This requirement is especially important when using resistor dividers connected to the analog inputs of the board. The resistance of the divider resistors cannot be too large.

Analog input software functions.

int analogRead(port)

Reads the voltage value at the specified analog input. An input voltage ranging from 0 to the voltage reference level (often 5 V) is converted to a code from 0 to 1023.

With a reference voltage of 5 V, the resolution is 5 V / 1024 = 4.88 mV.

It takes about 100 µs to convert.

int inputCode; // input voltage code
float inputVoltage; // input voltage in V

inputCod=analogRead(A3); // read voltage at input A3
inputVoltage= ((float)inputCod * 5. / 1024.); // conversion code to voltage (V)

void analogReference(type)

Sets the reference voltage for the ADC. It defines the maximum analog input voltage that the ADC can correctly convert. The value of the reference voltage also determines the coefficient for converting the code into voltage:

Input voltage = ADC code * reference voltage / 1024.

The type argument can take the following values:

  • DEFAULT - the reference voltage is equal to the controller supply voltage (5 V or 3.3 V). For Arduino UNO R3 - 5 V.
  • INTERNAL - internal reference voltage 1.1 V for boards with ATmega168 and ATmega328 controllers, for ATmega8 - 2.56 V.
  • INTERNAL1V1 - 1.1 V internal reference voltage for Arduino Mega controllers.
  • INTERNAL2V56 - 2.56V internal reference voltage for Arduino Mega controllers.
  • EXTERNAL – external reference voltage source, connected to the AREF input.

analogReference(INTERNAL); // reference voltage is 1.1 V

Two-channel voltmeter on Arduino.

As an example of using analog input functions, let's create a simple digital voltmeter project on Arduino. The device must measure voltages at two analog inputs of the board, and transfer the measured values ​​to a computer via a serial port. Using the example of this project, I will show the principles of creating simple systems for measuring and collecting information.

We decide that the voltmeter should measure voltage in the range of at least 0 ... 20 V and develop a circuit for connecting the voltmeter inputs to the Arduino UNO board.

If we set the reference voltage to 5 V, then the analog inputs of the board will measure the voltage in the range of 0 ... 5 V. And we need at least 0 ... 20 V. So we need to use a voltage divider.

The voltage at the input and output of the divider are related by the relation:

Uout = (Uin / (R1 + R2)) * R2

Transmission ratio:

K = Uout / Uin = R2 / (R1 + R2)

We need a 1/4 gain (20V * 1/4 = 5V).

To maintain maximum accuracy (10 digits), it is necessary that the internal resistance of the signal source does not exceed 10 kΩ. Therefore, we choose the resistor R2 equal to 4.22 kOhm. We calculate the resistance of the resistor R1.

0.25 = 4.22 / (R1 + 4.22)
R1 \u003d 4.22 / 0.25 - 4.22 \u003d 12.66 kOhm

I found resistors with a resistance of 15 kOhm with the closest rating. With resistors R1 = 15 kΩ and R2 = 4.22:

5 / (4.22 / (15 + 4.22)) = 22.77 V.

The voltmeter circuit based on Arduino will look like this.

Two voltage dividers are connected to analog inputs A0 and A1. Capacitors C1 and C2, together with the divider resistors, form low-pass filters that remove high-frequency noise from the signals.

I assembled this circuit on a breadboard.

I connected the first input of the voltmeter to a regulated power supply, and the second to the 3.3 V supply of the Arduino board. To control the voltage, I connected a voltmeter to the first input. It remains to write the program.

A program for measuring voltage using an Arduino board.

The algorithm is simple. Necessary:

  • read the ADC code twice per second;
  • convert it to voltage;
  • send the measured values ​​via the serial port to the computer;
  • The Arduino IDE port monitor program displays the obtained voltage values ​​​​on the computer screen.

I will give the sketch of the program in full.

// voltage measurement program
// on analog inputs A0 and A1

#include

measurement period time
#define R1 15. // resistor R1
#define R2 4.22 // resistor R2


float u1, u2; // measured voltages

void setup()(
Serial.begin(9600); //

MsTimer2::start(); // interrupt enable
}

void loop() (

// period 500 ms
if (timeCount >= MEASURE_PERIOD) (
timeCount=0;

//

// reading channel 2 code and converting to voltage
u2= ((float)analogRead(A1)) * 5. / 1024. / R2 * (R1 + R2);

// data transfer via serial port
Serial.print("U1 = "); Serial print(u1, 2);
Serial.print(" U2 = "); Serial.println(u2, 2);
}
}

// interrupt processing 1 ms
void timerInterupt() (
timeCount++;
}

Let me explain the line in which the ADC code is converted into voltage:

// reading channel 1 code and converting to voltage
u1= ((float)analogRead(A0)) * 5. / 1024. / R2 * (R1 + R2);

  • The ADC code is read: analogRead(A0) .
  • Explicitly converted to floating point format: (float) .
  • Converted to voltage at the analog input: * 5. / 1024. The dot at the end of the numbers indicates that this is a floating point number.
  • The divider ratio is taken into account: / R2 * (R1 + R2) .

Let's load the program into the board, start the serial port monitor.

Two running bars show the values ​​of the measured voltages. Everything is working.

Measuring the average value of the signal.

Let's connect the first channel of our voltmeter to a voltage source with a high level of ripple. We will see such a picture on the monitor.

The voltage values ​​of the first channel on the monitor screen are twitching and jumping all the time. And the readings of the control voltmeter are quite stable. This is because the reference voltmeter measures the average value of the signal, while the Arduino board reads individual samples every 500ms. Naturally, the moment of reading the ADC falls into different points of the signal. And at a high level of pulsations, the amplitude at these points is different.

In addition, if the signal is read in separate rare samples, then any impulse noise can introduce a significant error into the measurement.

The solution is to take several frequent samples and average the measured value. For this:

  • in the interrupt handler, we read the ADC code and sum it with the previous samples;
  • count the averaging time (the number of averaging samples);
  • when the specified number of samples is reached, we save the total value of the ADC codes;
  • to obtain the average value, we divide the sum of the ADC codes by the number of averaging samples.

A task from a 8th grade math textbook. Here is a sketch of the program, a two-channel average value voltmeter.

// medium voltage measurement program
// on analog inputs A0 and A1

#include

#define MEASURE_PERIOD 500 // measurement period time
#define R1 15. // resistor R1
#define R2 4.22 // resistor R2

int timeCount; // time counter
long sumU1, sumU2; // variables for summing ADC codes
long availabilityU1, availabilityU2; // sum of ADC codes (average * 500)
boolean flagReady; // sign of readiness of measurement data

void setup()(
Serial.begin(9600); // initialize the port, speed 9600
MsTimer2::set(1, timerInterupt); // timer interrupts, period 1 ms
MsTimer2::start(); // interrupt enable
}

void loop() (

if (flagReady == true) (
flagReady=false;
// conversion to voltage and transfer to a computer
Serial.print("U1 = ");
Serial.print((float)avarageU1 / 500. * 5. / 1024. / R2 * (R1 + R2), 2);
Serial.print(" U2 = ");
Serial.println((float)avarageU2 / 500. * 5. / 1024. / R2 * (R1 + R2), 2);
}
}

// interrupt processing 1 ms
void timerInterupt() (

timeCount++; // +1 averaging sample counter
sumU1+= analogRead(A0); // summation of ADC codes
sumU2+= analogRead(A1); // summation of ADC codes

// checking the number of averaging samples
if (timeCount >= MEASURE_PERIOD) (
timeCount=0;
availabilityU1=sumU1; // mean value overload
avarageU2=sumU2; // mean value overload
sumU1= 0;
sumU2=0;
flagReady=true; // sign measurement result is ready
}
}

/500, the number of samples, was added to the formula for converting the ADC code into voltage. Load, run the port monitor (Cntr + Shift + M).

Now, even with a significant level of ripple, the readings change by hundredths. This is only because the voltage is not stabilized.

The number of samples should be chosen taking into account:

  • the number of samples determines the measurement time;
  • the larger the number of samples, the smaller the influence of interference will be.

The main source of interference in analog signals is the 50 Hz network. Therefore, it is desirable to choose an averaging time that is a multiple of 10 ms - the half-cycle time of the network with a frequency of 50 Hz.

Optimization of calculations.

Floating point calculations simply devour the resources of an 8-bit microcontroller. Any floating point operation requires mantissa denormalization, fixed point operation, mantissa normalization, exponent correction... And all operations with 32 bit numbers. Therefore, it is necessary to minimize the use of floating point calculations. I will tell you how to do this in the next lessons, but let's at least optimize our calculations. The effect will be significant.

In our program, the conversion of the ADC code into voltage is written as follows:

(float)avarageU1 / 500. * 5. / 1024. / R2 * (R1 + R2)

How many calculations are there, and all are floating point. But most of the calculations are operations with constants. Line part:

/ 500. * 5. / 1024. / R2 * (R1 + R2)

(float)avarageU1 * 0.00004447756

Smart compilers themselves recognize calculations with constants and calculate them at compile time. I had a question how smart Andruino's compiler is. Decided to check.

I wrote a short program. It performs a cycle of 10,000 passes and then transmits to the computer the execution time of these 10,000 cycles. Those. it allows you to see the execution time of the operations placed in the loop body.

// calculation optimization check

int x= 876;
float y;
unsigned int count;
unsigned long timeCurrent, timePrev;

void setup()(
Serial.begin(9600);
}

void loop() (
count++;
// y= (float)x / 500. * 5. / 1024. / 4.22 * (15. + 4.22);
// y= (float)x * 0.00004447756 ;

if (count >= 10000) (
count=0;
timeCurrent=millis();
Serial.println(timeCurrent - timePrev);
timePrev= timeCurrent;
}
}

In the first variant, when floating-point operations are commented out and not executed in the loop, the program gave a result of 34 ms.

Those. 10,000 empty cycles are completed in 34ms.

Then I opened the line:

y= (float)x / 500. * 5. / 1024. / 4.22 * (15. + 4.22);

repeats our calculations. Result 10,000 passes in 922 ms or

(922 - 34) / 10,000 = 88.8 µs.

Those. this line of floating point calculations takes 89 µs to complete. I thought there would be more.

Now I closed this line with a comment and opened the next one, multiplied by a pre-calculated constant:

y= (float)x * 0.00004447756 ;

Result 10,000 passes in 166 ms or

(166 - 34) / 10,000 = 13.2 µs.

Amazing result. We saved 75.6 µs per line. Completed it almost 7 times faster. We have 2 such lines. But there can be much more of them in the program.

Conclusion - calculations with constants must be done by yourself on a calculator and used in programs as ready-made coefficients. The Arduino compiler will not calculate them at the compilation stage. In our case, we should do this:

#define ADC_U_COEFF 0.00004447756 // ADC code to voltage conversion factor

Serial.print((float)avarageU1 * ADC_U_COEFF, 2);

The optimal speed option is to transfer the ADC code to the computer, and with it all floating-point calculations. In this case, a specialized program should receive data on the computer. The port monitor from the Arduino IDE will not work.

I will talk about other ways to optimize Arduino programs in future lessons as needed. But without solving this issue, it is impossible to develop complex programs on an 8-bit microcontroller.

Another lesson appeared on the site () dedicated to measuring analog signals. It deals with the operation of the ADC in the background.

In the next lesson, we will learn how to work with the internal EEPROM, let's talk about data integrity control.

Category: . You can bookmark.

This article shows how to connect Arduino and PC and transfer data from ADC to PC. The Windows program was written using Visual C++ 2008 Express. The voltmeter program is very simple and has a lot of room for improvement. Its main purpose was to show the work with the COM port and the exchange of data between the computer and the Arduino.

Communication between Arduino and PC:

  • Taking readings from the ADC starts when the computer sends the 0xAC and 0x1y commands to the Arduino. at– ADC channel number (0-2);
  • The reading stops after the Arduino receives the commands 0xAC and 0x00;
  • During the reading, the Arduino sends the commands 0xAB 0xaa 0xbb to the computer every 50 ms, where aa and bb are the maximum and minimum measurements.

Program for Arduino

You can read more about serial communication at arduino.cc. The program is quite simple, most of it is working with a parallel port. After the end of reading data from the ADC, we get a 10-bit voltage value (0x0000 - 0x0400) in the form of 16-bit variables (INT). The serial port (RS-232) allows you to transfer data in packets of 8 bits. It is necessary to divide 16-bit variables into 2 parts of 8 bits.

Serial.print(voltage>>8,BYTE);

Serial.print(voltage%256,BYTE);

We shift the bytes of the variable 8 bits to the right and then divide by 256 and send the result to the computer.

You can download the full source code for Arduino software

Visual C++

I'm assuming you already have a basic knowledge of C++ programming for Windows, if not then use Google. The internet is full of tutorials for beginners.

The first thing to do is to add the serial port from the toolbar to the bottom form. This will allow you to change some important parameters of the serial port: port name, baud rate, bit rate. This is useful for adding controls to the application window, for changing these settings at any time, without recompiling the program. I only used the port selection option.

After searching for available serial ports, the first port is selected by default. How it's done:

array< String ^>^serialPorts=nullptr;

serialPorts = serialPort1->GetPortNames();

this->comboBox1->Items->AddRange(serialPorts);

this->comboBox1->SelectedIndex=0;

The serial port on a PC can only be used by one application at a time, so the port must be open before use and not closed. Simple commands for this:

serialPort1->Open();

serialPort1->Close();

To correctly read data from the serial port, you must use events (in our case, interrupt). Select event type:

Drop-down list when double-clicking "DataReceived".

The event code is generated automatically:

If the first byte arrived on the serial port is 0xAB, if that means the rest of the bytes carry voltage data.

private: System::Void serialPort1_DataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e) (

unsigned char data0, data1;

if (serialPort1->ReadByte()==0xAB) (

data0=serialPort1->ReadByte();

data1=serialPort1->ReadByte();

voltage=Math::Round((float(data0*256+data1)/1024*5.00),2);

data_count++;

serialPort1->ReadByte();

Writing and reading serial port data

For me, a small problem was to send hex RAW data through the serial port. The Write() command was used; but with three arguments: array, start byte number, number of bytes to write.

private: System::Void button2_Click_1(System::Object^ sender, System::EventArgs^ e) (

unsigned char channel=0;

channel=this->listBox1->SelectedIndex;

array^start =(0xAC,(0x10+channel));

array^stop =(0xAC,0x00);

serialPort1->Write(start,0,2);

this->button2->Text="Stop";

) else (

serialPort1->Write(stop,0,2);

this->button2->Text="Start";

That's all!

Original article in English (translation: Alexander Kasyanov for cxem.net site)