Multi-channel Wifi Voltage & Current Meter




Introduction: Multi-channel Wifi Voltage & Current Meter

When breadboarding, one often needs to monitor different parts of the circuit at once.

To avoid the pain having to stick the multimeter probes from one place to another, I wanted to design a multi-channel voltage and current meter.

The Ina260 board from Adafruit provides a very effective and efficient way for doing so. It contains a very accurate voltage and current meter with an integrated I2C bridge (saving a lot of pins when combining 3 of them!).

The only thing missing was a display. Hence the decision to hook the boards up onto an ESP32 development board, which can easily carry a webserver to present the measured items on a PC/mobile screen.


3 x Ina260 adafruit board

3 x header pins with long pins

Min 6 jumper wires

1 x ESP32 Wrover-B (or any Wifi other board with I2C support)

2 x 19 pin pin header (if applicable)

1 x PCB or Perfboard

1 x 3.3 V power supply

Step 1: Solder the Three INA Boards

The first step is to assemble the three INA260 boards.

A very good instruction can be found on the Adafruit website. Follow the Adafruit assembly instructions.

To be able to stack them on top of each other use the long pin headers rather than the delivered pin strips!

Step 2: Configure Three Different I2C Addresses

I2C is a serial protocol for a two-wire interface to connect low-speed devices over short distances. One can connect up to 127 slaves. On one bus every device needs to be identified by a unique I2C address. The I2C address of a device is often hard wired into the chip of a device. To connect the same devices on one bus, the manufacturer often leaves the possibility for changing the I2C address by soldering a configuration of pins together.

This is also the case for the three INA260 boards. The device has two address pins, A0 and A1 that can be connected to GND, VS, SCL or SDA to set the desired address. In the datasheet of the INA260 chip from texas instruments, one can find the list of pin connections for each of the 16 possible addresses.

The adafruit board limits this to 4 boards by exposing two pads which can be used to pull the A0 and/or A1 to VS. The default address of the INA260 board is 0x40.

You complete this step by assigning different addresses to the two other boards:

By soldering the A0 pad of the second board you set his address to: 0x41 (or 1000001 BIN)

By soldering the A1 pad of the third board you assign the address to: 0x44 (or 1000100 BIN)

Step 3: Connect the Ina Boards to the ESP32

Now that we have assigned different I2C addresses to each of the INA boards, it is time to connect them to the ESP32 board!

Per the above picture, connect

1) the VCC pin to the 3.3V pin

2) the GND pin to the GND pin

3) the SDA pin to GPIO pin 21

4) the SCL pin to GPIO pin 22

I have used a PCB design to make the connections as it is part of a larger project (a WiFi adjustable voltage supply with adjustable current limiting - hope to make an instructable for this one as well).

You may use any other way for connecting, this might be a perfboard you solder up or using a breadboard. Both of those will work fine as well.

Step 4: Install the ESP32 Board in the Arduino IDE

Now that we have connected the boards to each other, it is time to check the connection.

We will do that by collecting the I2C addresses of the Ina boards.

The ESP32 board works perfectly with the Arduino IDE.

So let us install the ESP32 board in Arduino using the Board manager.

Step 5: Check the Ina to ESP32 Connection Using I2C Scanner

We will use a simple I2C address scanner to ensure connectivity between the ESP32 and the Ina260 boards.

The I2C address scanner code can be copied pasted into an empty Arduino project.

The code has been taken from the Arduino cc website:

// --------------------------------------
// i2c_scanner // // Version 1 // This program (or code that looks like it) // can be found in many places. // For example on the forum. // The original author is not know. // Version 2, Juni 2012, Using Arduino 1.0.1 // Adapted to be as simple as possible by user Krodal // Version 3, Feb 26 2013 // V3 by louarnold // Version 4, March 3, 2013, Using Arduino 1.0.3 // by user Krodal. // Changes by louarnold removed. // Scanning addresses changed from 0...127 to 1...119, // according to the i2c scanner by Nick Gammon // // Version 5, March 28, 2013 // As version 4, but address scans now to 127. // A sensor seems to use address 120. // Version 6, November 27, 2015. // Added waiting for the Leonardo serial communication. // // // This sketch tests the standard 7-bit addresses // Devices with higher bit address might not be seen properly. // #include void setup() { Wire.begin(); Serial.begin(9600); while (!Serial); // Leonardo: wait for serial monitor Serial.println("\nI2C Scanner"); } void loop() { byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 1; address < 127; address++ ) { // The i2c_scanner uses the return value of // the Write.endTransmisstion to see if // a device did acknowledge to the address. Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) Serial.print("0"); Serial.print(address,HEX); Serial.println(" !"); nDevices++; } else if (error==4) { Serial.print("Unknown error at address 0x"); if (address<16) Serial.print("0"); Serial.println(address,HEX); } } if (nDevices == 0) Serial.println("No I2C devices found\n"); else Serial.println("done\n"); delay(5000); // wait 5 seconds for next scan }

Step 6: Creating the HTML Webserver Folder

The ESP32 provides the possibility for running a webserver. It also provides a quite large RAM memory to hold some webpages. (It automaticlly compresses the webpage files).

The Arduino IDE provides the functionality for uploading the created webpages to the ESP32's RAM directly.

In order to do this, you need to create a folder 'data' underneath the Arduino project's folder. In my case this is \Arduino\esp32_Power_supply_v1_implemented\data.

It is important to name the folder exactly 'data' as it is the folder name Arduino will look for when uploading the webpage files to the ESP.

Step 7: Create the Power Monitor Webpage

HMTL is a language that allows to present a text into a webbrowser. A HTML file is saved under the extension htm(l). The formatting of a webpage is usually placed into a seperate file (eg. css file) . The program functionality that a webpage needs to offer is normally placed into another file (eg. js file, for javascript).

In my HTML document I included the text, formatting and Javascript into one file. It is therefore not a good example of how to make a webpage but it serves the purposes. I named the HTML document 'Index.htm' .

A second file is included in my datafolder, ie. PicoGraph.js. The PicoGraph library is provided by Vishnu Shankar B from RainingComputers and allows for a very simple, yet effective and flexible way for presenting graphs onto a webpage. I slightly amended the code to serve better my purpose.

You will notice that the HTML webpage also includes the code for controlling the voltage supplies on my PCB board. The voltage supply code controls the voltage level of 5 I/O pins. You can change the code to exclude this or you may leave it in if no impact.

The html code is attached to this step as a txt file (as the instructables doesnt allow uploading htm code).

In order to use the HTML code you copy and paste it into a text editor (I use Notepad++) and save it as 'Index.htm' under the 'Data' folder. You do the same for the picograph.txt file but rename it to picograph.js

With regard to the HTML file:

A function SndUpdate is used to send messages back and forward from the ESP to the webpage.

The messages send from the ESP are serving the power supply functionality and is out of scope for this instructable. the messages to the ESP are serving the Ina260 board measurements.

<p>var Msg = JSON.parse(xh.responseText);<br>


The above code reads 6 numbers from the ESP32 board, ie. the voltage measurement, the current measurement from the first board, followed by the two measurements from the second and so forth.

The graphs are embedded in so-called flex-containers, which allow for flexible resizing of the webpage.

<p>.flex-container {<br>  display: flex;
  background-color: cadetblue;
  flex-wrap: wrap;
.flex-container > div {
  background-color: #f1f1f1;
  margin: 10px;
  padding: 20px;
  font-size: 20px;
  font-family: "Seven Segment";
  font-weight: bold;

The content of each of the flex containers is made up as followed, including the embedded graphs.

(note that the < > where removed)

<p>div id="PG1_rect"<br>	label for="PG1_scale"Scale:/label
	input type="number" id="PG1_scale" name="PG1_scale" value="10"brbr
	!-- Canvas for thr graph --
	canvas id="PG1_graphDemo" style="height:100px; border:2px solid #000000;background-color: #fafafa;" 
	/canvas</p><p>	!-- div for legends/labels --
	div id="PG1_graphLabels"
	div id="PG1_wattLabel"

The last section of importance in the HTML file works with the PicoGraph library to present the numbers:

	var PG1_demograph = createGraph("PG1_graphDemo",["Ch1"],"V", "PG1_graphLabels", 20, 11, false, false,11,"#e52b50");<br>
	var PG2_demograph = createGraph("PG2_graphDemo",["Ch1"],"mA", "PG2_graphLabels", 20, 11, false, false,11,"#e52b50");
	var PG3_demograph = createGraph("PG3_graphDemo",["Ch2"],"V", "PG3_graphLabels", 20, 11, false, false,11, "#008000");
	var PG4_demograph = createGraph("PG4_graphDemo",["Ch2"],"mA", "PG4_graphLabels", 20, 11, false, false,11, "#008000");
//	var PG5_demograph = createGraph("PG5_graphDemo",["Ch3"],"V", "PG5_graphLabels", 20, 11, false, false,11, "#0000ff");
	var PG6_demograph = createGraph("PG6_graphDemo",["Ch3"],"mA", "PG6_graphLabels", 20, 11, false, false,11, "#0000ff");

 /* Update values every second */
        setInterval(updateEverySecond, 1000);
function updateEverySecond() { /* Get new values */
        /* Update graph */
	parseInt(byID("PG3_scale").value)/10, "#008000");
	parseInt(byID("PG4_scale").value)/10, "#008000");
//	PG5_demograph.update([PG5_yrand0],parseInt(byID("PG5_scale").value)+
//	parseInt(byID("PG5_scale").value)/10, "#0000ff");
	parseInt(byID("PG6_scale").value)/10, "#0000ff");
	var Watts = Math.round(PG1_yrand0 * PG2_yrand0 *100)/100;
	byID("PG1_wattLabel").innerHTML = ` WATT:  ${Watts}  mW `;
	var Watts = Math.round(PG3_yrand0 * PG4_yrand0 *100)/100;
	byID("PG3_wattLabel").innerHTML = `WATT:   ${Watts} mW`;
//	var Watts = Math.round(PG5_yrand0 * PG6_yrand0 *100)/100;
//	byID("PG5_wattLabel").innerHTML = `WATT:   ${Watts} mW`;
	byID("PG1_scale").value = Math.floor(parseInt(byID("PG1_scale").value)/2+PG1_yrand0);
	byID("PG2_scale").value = Math.floor(parseInt(byID("PG2_scale").value)/2+PG2_yrand0);
	byID("PG3_scale").value = Math.floor(parseInt(byID("PG3_scale").value)/2+PG3_yrand0);
	byID("PG4_scale").value = Math.floor(parseInt(byID("PG4_scale").value)/2+PG4_yrand0);
//	byID("PG5_scale").value = Math.floor(parseInt(byID("PG5_scale").value)/2+PG5_yrand0);
	byID("PG6_scale").value = Math.floor(parseInt(byID("PG6_scale").value)/2+PG6_yrand0);<br>

Studying the code you will notice i am only using 5 graphs of the 6 for my purpose. Un-commenting the right lines will enable the 6th graph.

For those not experienced with html, this step can come over as hard. However it can serve as a nice introduction into the world of HTML. I know because this was the first page I ever created. So don't be scared. For those experienced under us, be forgiving.

The result of your work on the webpage can be reviewed by opening your html, it will load into your browser and show its appearance. You can check for possible errors by pressing the F12 key in your browser, the debugging window will show up. Full explanation of how to debug is out of the scope of this instructable, but the webpage can be helpful as first step to webpage / javascript debugging.

Next step is to load the created webpages into the ESP32.

Step 8: Load Webpage Into the ESP32

After obtaining a satisfactory result, it is time to upload the webpage into the ESP32.

You do this by saving the 'Index.htm' (your webpage) and the 'PicoGraph.js' into the 'data' folder under your Arduino project.

Next step is to connect the ESP32 board onto the computer. After having selected the correct board and the COM port, select the ESP32 Sketch Data Upload under the Tools menu in the Arduino IDE.

You will see that the IDE will begin its uploading process, which should lead to a succesfull load.

Next to this step is to configure the ESP32 microcontroller as a webserver.

Step 9: Configure the ESP32 As Webserver

Attached you find the Arduino Ino sketch that will configure the ESP32 as a webserver.

You will need to replace the SSID and related Password with your router's password.

As already mentioned this sketch also contains the code to configure the webpage as a controller for the power supply side of the PCB (in fact, configuring 5 IO pins as PWM pins and controlling them through the message stream from the webpage).

The sketch is based on the standard Webserver sketch developped by Hristo Gochkov.

Some explanation on the code.

The following functions are all related to setting up the webserver.

String formatBytes(size_t bytes)
String getContentType(String filename) bool exists(String path) bool handleFileRead(String path) void handleFileUpload() void handleFileDelete() void handleFileCreate() void handleFileList()

Also the first code in the setup() function is related to PWM and Webserver setup.

The following code sets the Interrupt function that serves the message streams to and from the webpage:

(you should recognize the identifiers from the webpage creation)

server.on("/SndUpdate", HTTP_GET, [] () {

String Msg = "{";
Msg+="\"PG1_yrand0\":"+ (String) Vina[1]; Msg+=",\"PG2_yrand0\":"+ (String) Iina[1]; Msg+=",\"PG3_yrand0\":"+ (String) Vina[0]; Msg+=",\"PG4_yrand0\":"+ (String) Iina[0]; Msg+=",\"PG5_yrand0\":"+ (String) Vina[2]; Msg+=",\"PG6_yrand0\":"+ (String) Iina[2]; Msg+="}";

server.send(200, "text/json", Msg);

This starts the server:


The next block of code, initializes the INA260 boards:

//INA260 initialization
if (!ina260_0x40.begin(0x40)) { Serial.println(F("Couldn't find INA260 0x40 chip")); //while (1); } Serial.println(F("Found INA260 chip 0x40")); if (!ina260_0x41.begin(0x41)) { Serial.println(F("Couldn't find 0x41 INA260 chip")); //while (1); } Serial.println(F("Found INA260 0x41 chip")); if (!ina260_0x44.begin(0x44)) { Serial.println(F("Couldn't find INA260 0x44 chip")); //while (1); } Serial.println(F("Found INA260 chip 0x44"));

ina260_0x40.setAveragingCount(INA260_COUNT_256); ina260_0x40.setVoltageConversionTime(INA260_TIME_1_1_ms); ina260_0x40.setCurrentConversionTime(INA260_TIME_1_1_ms); ina260_0x40.setMode(INA260_MODE_CONTINUOUS); ina260_0x41.setAveragingCount(INA260_COUNT_256); ina260_0x41.setVoltageConversionTime(INA260_TIME_1_1_ms); ina260_0x41.setCurrentConversionTime(INA260_TIME_1_1_ms); ina260_0x41.setMode(INA260_MODE_CONTINUOUS); ina260_0x44.setAveragingCount(INA260_COUNT_256); ina260_0x44.setVoltageConversionTime(INA260_TIME_1_1_ms); ina260_0x44.setCurrentConversionTime(INA260_TIME_1_1_ms); ina260_0x44.setMode(INA260_MODE_CONTINUOUS);

In the Loop code, the following statement assures handling of the interrupt code:


The following code in the loop statement is related to the Power supply functionality.

The following code in the loop() is interesting again:

Iina[0]=ina260_0x40.readCurrent(); Vina[1]=ina260_0x41.readBusVoltage()/1000.0f; Iina[1]=ina260_0x41.readCurrent(); Vina[2]=ina260_0x44.readBusVoltage()/1000.0f; Iina[2]=ina260_0x44.readCurrent();

These statements collect and set ready the measurements for transfer to the webpage via the Server.on interrupt calls (occuring every 1000ms, set in the webpage html java script).

Step 10: You Are Done!

Uploading the sketch into the ESP32 board, should finalize the setup and your Power monitor should be final!

You might have noticed that powering the ESP32 is now done via the USB port, this elevates a large part of the advantages of the WiFi based connection with your voltage / current meters. Hence, I have made a simple LM317 based voltage regulated power supply for the ESP32. I kept it out of the scope of this instructable but if there is interest could become a next instructable.

In the next step I already have provided the electronic circuit for the power supply which could serve as inspiration.

Step 11: Powering the ESP32

Herewith an inspiration to build a stand alone powersource for your ESP32, if you dont have one lying around.

The power circuit works of a 19V laptop power supply. This asks for a two phased voltage step down to keep the Power dissipation of the LM317's under control. (Even with heat sinks!). Also dont forget to inlcude a 100uF capacitor in front of the VCC_ESP line as those microcontrollers have large current swings certainly when starting up the WiFi connection.

Note to not power the ESP32 with more than one power source at a time!

Further the usual liability disclaimers, but most of all

Have fun!

All the files can be found on my GitHub:

Be the First to Share


    • Game Design: Student Design Challenge

      Game Design: Student Design Challenge
    • Make It Bridge

      Make It Bridge
    • For the Home Contest

      For the Home Contest


    Elaina M
    Elaina M

    2 years ago

    Fun and useful project - thanks for sharing !