Voltage measurement

Voltage measurement the Internet of Things way

The voltage can be measured with a simple ADC and transmitted over wifi to SignalK running on the Server where both OpenPlotter and SignalK is running. For this one need a module like Arduino but with wiif built in. The ESP8266 is such a module. It has a single analog input providing 10 bits analog to digital conversion (ADC). Running on 3.3 Volt it accept 0-3.3 Volt voltage input. I module also run on a range of supply voltages 5-12 so power supply is not very demanding. The setup for the ESP-32 is essential the same, only that the ESP-32 has many ADC inputs.

The hardware is even simpler that most other setups, the picture show a breadboard with the ESP8266 on and the resistor ladder (100k+22k) bringing down 0-18 Volt to 0-3.25 Volt be be within the ESP8266's range. The rest is just a matter of programming the ESP8266. I have used Arduino IDE programming environment, not because I love C++ that much, but because I know the IDE. A Python programming environment might be an easier alternative.

The code for this first example of using the simple ESP 8266 is found in my Github repository.

I also include a few Python scripts in order ot test the ESP8266 module, both TCP and UDP connections. SignalK uses UDP IP packages.

The code for the most interesting part of the IDE sketch look like this:

val=analogRead(A0); // Read the analog input on the ESP2866.

U=((double)val/(double)1023.0)*19.576; // Calibrated using a multimeter, check this value carefully.

dtostrf(U,3,2,Us); // double to string convert

and more signalK oriented :

cmd = "{\"updates\": [{\"$source\": \"ESP8266\",\"values\":[ {\"path\":\"electrical.service.voltage\",\"value\":";

cmd += Us; cmd+="}]}]}\0"; // Not sure if NULL is needed or not, but it's good custom to terminate with NULL:

// Some tricks to convert from String to character array as Udp.write will not accept string type argument.

char cmd2[cmd.length()+1]; // Counting from zero, add one.

Udp.beginPacket(host,port); // Connect to to server and prepare for UDP transfer.

strncpy(cmd2,cmd.c_str(),sizeof(cmd2)); // Convert from String to array of characters.

// Serial.println(cmd2); Serial.print(" Message har length: "); Serial.println(sizeof(cmd2));

Udp.write(cmd2); // Send the message to the SignalK server.

Udp.endPacket(); // End the connection.

Older wired versions

Wired approach - the wish for galvanic isolation

To measure the voltage on the high amp service power supply onboard a yacht with little change og danger in case of a short between the computer system and the 12/24 V line power some care most be taken. Providing galvanic isolation for DC voltage is not so simple, for AC a simple transformer will do the trick. A simple solution is to provide a ladder of resistors preventing any significant amount of current flowing in any case for short circuit. In the example below the 12 Volt is connected to a grounded ladder of 22k+10k+22k = 54k. In the worst case scenario a resistor of 22k will limit the short circuit current to 0.55 mA. Measurement is made over the central 10k resistor with differential connection to the ADS 1115. This prevent any connection of 0V for the computer system to be isolated from the GND/0V of the service power supply onboard. A single ended measurement would result in either the +12V or the 0V/GND of the high power 12 (or even 24)V into the computer system circuit.

Another more secure and elegant (but more complex) is to use an isolation amplifier, it can accept 0-5 V input signal and is amplification is set to unity it provide a 0-5V signal totally (several kV) isolated from the point of measure. This 0-5 V signal can be used as is (ADS1115 has a setting for 4.096V full range) or divided by a resistor ladder to yield anything below 5V as max (a MCP3008 using a 2.4V voltage reference as full range will require the voltage to be divided by 2). As the analog device is usable over a smaller rage the input to the device can be set at the first resistor ladder, providing the final voltage right away. using only a portion of the full range 0-5V introduce some noise, but not in range of we were to use only a portion of the full range of a 10 bit ADC.

Galvanic isolation 12/24 Volt measurement

In order to have a isolation between the computer system and the high power 12/24V system a cleaver device is needed. One option is an amplifier from Analog Devices, AD202, this device can take 0-5V as an input and provide 0-5V as output while maintaining isolation in the kV range between the input and output.

The picture on the right show how this device works. The two transformers provide the galvanic isolation between the input and the output. The connections are really simple, an input for a voltage in the 0-5V range, an output for the same range (other amplifications than unity is possible) and a +15V power supply. As 15V is not easily available a small 5 to 15V converter can be utilized.

The only negative with this device is it's relatively high price, the isolation amplifier has a price tag in the 20 Euro range. However, it's a small price for safety from a total burnout of the computer system in case of a short circuit.


To provide an input in the 0-5V interval from 12V one need a resistor ladder of two resistors to provide a suitable input voltage for the analog to digital converter, the AD202 can take inputs from 0 to 5V. In the case of the 10 bit MCP3008 ADC the voltage range is set by a voltage reference, LM385-2.4, 2.4V or LM4030-4.096V / MCP1541 (both 4.096 Volt) are a popular references while the ADS1115 can be programmed to 1.024, 2.048 or 4.096 Volt range.

The MCP3008 has 10 bits which is 1023 as max value. This yield, if we assume 20V maximum value possible an accuracy of 20/1023 which is 0.02 Volt. This is a resolution of about 2% which might be ok for some applications. If we want higher resolution we can go for the 16 bit ADS1115 and the numbers will look like: 20/65535 = 0.0003 Volt or 0.03% resolution. However, the ADS1115 has only 4 analog inputs while the MCP3008 has 8.

None isolated examples, older designs short circuit safe, but not isolated and is not recommended for usage with high power 12V :

Breadbord example

The example below show how the voltage measurement are arranged. The ADS 1115 is set up to measure in differential mode:

For more information about ADS 1115 and the page about measurement of current. In this setup any fluctation of the +5V supply voltage will not have any effect.

Isolated example using a isolation amplifier, playing it safe :

The picture to the left show the usage of a AD202 isolation amplifier. It accept a voltage from 0 to 5 as input and deliver the same voltage on the output (gain i set to 1 by connecting pin 3 and 4). Amplification is possible by using a resistor network, see manuals for more information, reference above.

In this setup voltage on the high amp high power 12V service supply can be measured safely without any problems of letting the service potentials into the computer system. This would work just as well for a 230 V live mains power supply. The isolation within the AD202 is in the kV range (depending on model).

The AD202 require a 15V power supply and hence a small buck converter are employed to upconvert the 5V power supply from the Raspberry to the 15V required for the AD202. This is a small simple device available from many sources and easily available.

The output is then fed directly into the ADS115 and imported into OpenPlotter and SignalK. This is documented under current measurement and the code is found in github, code for measuring both current and voltage. The usage of the isolation amplifier does not change anything as the gain is set to unity, it just isolate the high power 12V service voltage from the delicate computer circuits. There is only one issue on the negative side, the price of the AD202 is somewhat less than 20 Euro.

The conversion from ADC count to voltage can be done with a Python function:

def get_u():

val=adc.read_adc_difference(3, gain=GAIN_U, data_rate=SAMPLES_U)


# 10k/54k=0.185185 ; rounded to 0.1841 after cal with voltmeter.


if u<0.000001: # So close to zero that we flush to zero.


return u