
The RMS RPI MOD |
||
- Description
- Parts List
- RMS to DC Convertion
- 12-bit Analog to Digital Convertion
- Photos
- Software
- Contact Us
The RMS RPI MOD is a Raspberry Pi © current measurement add on. Using a split-core transformer it converts RMS to DC then to digital .
In order to measure AC current one must at least convert the input to digital. Then take enough samples to get an accurate reading, the more the better. Then detect the zero crossing and run the RMS (Root Mean Square) math on the data. This is very CPU intensive and when measuring multiple circuits it maybe impossible to complete in a reasonable amount of time.
With the RMS RPI MOD and a YHDC split-core transformer RMS output conversion is handled by a Linear Technology LTC1966 RMS to DC converter. The DC output of the LTC1966 is then fed to the input of a Microchip MPC3221 12-Bit A/D Converter with I2C Interface. Now simply wait for the conversion to complete and read a single sample.
Quantity | Part | Reference |
---|---|---|
1 | Buy the board at OSHPARK | Circuit board |
2 | LTC1966 | U1,U3 |
2 | MCP3221 | U2,U4 |
4 | .1 µf 16V ±10% Capacitors | C1,C3,C4,C6 |
4 | .47 µf 16V ±20% Capacitors | C2,C5 |
2 | 2K Ohm 1/4W ±1% Resistors | R4,R5 |
2 | 180 Ohm 1/4W ±5% Resistors | R1,R6 |
4 | 100K Ohm 1/4W ±5% Resistors | R2,R3,R7,R8 |
2 | 3.50mm (0.141", 1/8", Mini Plug) | CON1,CON2 |
1 | Raspberry Pi GPIO Stacking Header | RPi1 |
The function of the LTC1966
RMS (Root Mean Square) to DC (Direct Current) conversion is a process used in electrical and signal processing systems to represent the effective value of an AC (Alternating Current) signal as an equivalent DC value. This is important because the RMS value of an AC signal represents the amount of power it can deliver, equivalent to the power delivered by a DC signal of the same value.
Key Concepts
-
RMS Value:
- RMS is the square root of the average of the squares of the instantaneous values of a periodic waveform.
- It is calculated as:
where TT is the time period of the waveform and v(t)v(t) is the instantaneous voltage.
-
DC Equivalent:
- The DC value is a constant voltage that produces the same heating effect (or power) in a resistive load as the AC signal would.
- For purely resistive loads, the DC value equivalent to an RMS value is the RMS value itself, because RMS already represents the power equivalency.
RMS to DC Conversion for Different Waveforms
The RMS to DC conversion depends on the waveform type:
-
Sine Wave: The RMS value of a sine wave is:
-
Square Wave: For a square wave with constant amplitude:
Here, the RMS value and DC equivalent are the same.
-
Triangular Wave: The RMS value for a triangular wave is:
The DC equivalent is simply VRMSVRMS as it represents the effective power delivery.
RMS to DC Conversion Circuit
In practical systems, RMS-to-DC conversion can be achieved using:
-
Analog Circuits:
-
Digital Signal Processing (DSP):
- Sampling the AC waveform digitally, squaring the samples, averaging them, and then taking the square root.
- This method is more flexible and precise, often used in software implementations.
Practical Applications
- Power Measurement: RMS values are used to measure the effective power of AC signals in electrical systems.
- Signal Analysis: RMS-to-DC conversion is crucial in analyzing the energy content of signals in communication and audio processing.
The function of the MCP3221
A 12-bit Analog-to-Digital Converter (A/D or ADC) is an electronic device that converts an analog signal, such as voltage, into a digital representation using 12 bits of resolution. This type of ADC is widely used in applications requiring moderate precision and fast sampling, such as in microcontrollers, data acquisition systems, and digital oscilloscopes.
Key Features of a 12-bit ADC
-
Resolution:
-
A 12-bit ADC divides the analog input range into
discrete levels. -
For example, if the input range is 0–5V, the smallest distinguishable voltage increment (step size) is:
-
A 12-bit ADC divides the analog input range into
-
Sampling Rate:
- Determines how frequently the ADC samples the analog signal.
- Higher sampling rates are required for signals with high-frequency components (as per the Nyquist theorem).
-
Input Range:
- Typically defined as 0–X volts or ±X volts, depending on the ADC's design and the application.
- The input range may be configurable in some ADCs using reference voltages.
-
Accuracy and Precision:
- Accuracy: How closely the digital output matches the true analog signal.
- Precision: Repeatability of the ADC’s conversion results for the same input.
-
Conversion Time:
- Time required for the ADC to convert an analog signal to a digital value.
- This is influenced by the ADC architecture (e.g., successive approximation, flash, sigma-delta).
ADC Architectures Used in 12-bit Converters
-
Successive Approximation Register (SAR):
- Common for 12-bit ADCs due to a good balance between speed and power consumption.
- Converts the input by successively narrowing down the range of possible values using a binary search algorithm.
-
Sigma-Delta ADC:
- Offers high resolution and is used for applications needing high accuracy over slower conversion rates.
- Converts the signal through oversampling and digital filtering.
-
Flash ADC:
- Extremely fast but uses a large number of comparators.
- Less common for 12-bit ADCs because it is more expensive and consumes more power.
Applications of 12-bit ADCs
-
Microcontrollers:
- Used for sensing and digitizing inputs like temperature, pressure, and light intensity.
-
Audio and Signal Processing:
- Sampling signals for digital processing or playback.
-
Instrumentation and Control Systems:
- High precision and moderate-speed applications, such as motor control or industrial automation.
-
Data Acquisition Systems:
- Used in laboratories and research to measure and digitize physical phenomena.
Factors Affecting Performance
-
Noise and Signal-to-Noise Ratio (SNR):
- Lower noise results in better SNR, improving the ADC's effective resolution.
-
Quantization Error:
- Inherent in all ADCs, arising from mapping continuous signals to discrete levels.
-
Linear and Differential Nonlinearity (INL/DNL):
- Measures how accurately the ADC steps match ideal linear steps.
-
Reference Voltage Stability:
- The accuracy of the ADC is influenced by the precision of the reference voltage.
A 12-bit ADC provides a good compromise between resolution and speed, making it suitable for a wide range of applications in embedded systems and data acquisition tasks. Let me know if you’d like to dive deeper into a specific ADC architecture or application!
Photos
Language
Note this software is written for the 30Amps/Volt YHDC split-core transformer. [YHDC Data Sheet]
C++ Software
First install wiringPi.
wiringPi
Command Lines
g++-4.7 -std=c++11 -c -I/usr/local/include -o amps.o amps.c++ g++-4.7 -std=c++11 -I/usr/local/include -o amps amps.o -L/usr/local/lib -lwiringPiDownload Header
Download Source
Header
/*
* amps.h
*
* Acme Software Works
*/
/*************************************************************/
/*
* Example code for the Acme Software Works RMS RPI MOD
*/
/*************************************************************/
#ifndef _amps_h_
#define _amps_h_
#define OPTIONS "h?dc:"
#define USAGE "Usage: %s [-d (debug)] [-c 1|2 (channel)]\n"
/*************************************************************/
using namespace std;
/*************************************************************/
/* Classes */
class Amps {
int fd;
bool debug;
char time_str[26];
int channel;
double amps;
public:
Amps(int channel, bool debug = false) { init(channel,debug); };
enum { CHANNEL1 = 0x4d, CHANNEL2 = 0x4e };
void init(int channel, bool debug = false);
double readAmps(void);
const char* getTimeStr(void);
void printAmps() {
cout << getTimeStr() << " amps: " << setprecision(2) << amps << '\n';
};
};
/*************************************************************/
#endif /* _amps_h_ */
Implementation
/*
* amps.c++
*
* Acme Software Works
*
*/
/*************************************************************/
/*
*
* Example code for the Acme Software Works RMS RPI MOD
*
*/
/*************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <inttypes.h>
#include <iostream>
#include <iomanip>
#include <wiringPi.h>
#include <wiringPiI2C.h>
/*************************************************************/
#include "amps.h"
/*************************************************************/
/*
* Needed for command line parsing (getopt())
*/
extern char* optarg;
extern int optind;
char* my_name;
/*************************************************************/
/*
* main
*
*/
main(int argc,char* argv[])
{
int c;
bool debug = false;
int channel = Amps::CHANNEL1;
/* Get command line opts */
if((my_name = strrchr(argv[0],'/')) == NULL) {
my_name = argv[0];
} else ++my_name;
while((c = getopt(argc,argv,OPTIONS)) != -1) {
switch(c) {
case 'c' :
{
int ch = atoi(optarg);
if ( ch == 1 ) channel = Amps::CHANNEL1;
else if ( ch == 2 ) channel = Amps::CHANNEL2;
else {
fprintf(stderr,"No such channel\n");
fprintf(stderr,USAGE,my_name);
exit(1);
}
}
break;
case 'd' :
debug = true;
break;
case '?' :
fprintf(stderr,USAGE,my_name);
exit(1);
}
}
argc -= optind;
argv += optind;
{
Amps monitor(channel,debug);
for(;;) {
sleep(1);
monitor.readAmps();
monitor.printAmps();
}
}
exit(0);
} /* End of main() */
/*************************************************************/
const char* Amps::getTimeStr()
{
time_t result = time(NULL);
strftime(time_str,sizeof(time_str),"%FT%T",localtime(&result));
return time_str;
} // End of getTimeStr()
/*************************************************************/
double Amps::readAmps()
{
uint8_t buf[3];
int16_t val;
double rms_val = 0.0;
wiringPiI2CWrite(fd,0xab);
if (read(fd, buf, 2) != 2) {
perror("Read conversion");
return -1.0;
}
val = (int16_t)buf[0]*256 + (uint16_t)buf[1];
rms_val = (double)val*(5.0/(double)4096);
if ( debug )
cout << getTimeStr() << " volts: " << setprecision(4) << rms_val << '\n';
amps = 30.0*rms_val; // 30 amps per volt split-core transformer
return amps;
} // End of readAmps()
/*************************************************************/
void Amps::init(int channel,bool debug)
{
this->debug = debug;
this->channel = channel;
if (wiringPiSetup() == -1) {
cout << "Setup failed" << '\n';
exit(1);
}
// MCP3221 A5 | A6
if ((fd = wiringPiI2CSetup(channel)) == -1) {
cout << "I2C Setup failed" << '\n';
exit(1);
}
} // End of init()
/*************************************************************/
/******************** END OF FILE ****************************/
/*************************************************************/
C Software
First install wiringPi.
wiringPi
Command Lines
gcc-4.7 -std=c11 -c -I/usr/local/include -o amps.o amps.c
gcc-4.7 -std=c11 -I/usr/local/include -o amps amps.o -L/usr/local/lib -lwiringPi
Download
/*
* amps.c
*
* Acme Software Works
*
*/
/*************************************************************/
/*
*
* Example code for the Acme Software Works RMS RPI MOD
*
*/
/*************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <inttypes.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
/*************************************************************/
// Function Definitions
const char* getTimeStr();
double readAmps(int fd,int debug);
int init(int channel);
/*************************************************************/
/*
* Needed for command line parsing (getopt())
*/
int getopt(int argc, char * const argv[], const char *optstring);
extern char* optarg;
extern int optind;
char* my_name;
/*************************************************************/
/*
* main
*
*/
int main(int argc,char* argv[])
{
const char *USAGE = "Usage: %s [-d (debug)] [-c 1|2 (channel)]\n";
// MCP3221 I2C addresses
int const CHANNEL1 = 0x4d;
int const CHANNEL2 = 0x4e;
int c;
int debug = FALSE;
int channel = CHANNEL1;
int fd = -1;
/* Get command line opts */
if((my_name = strrchr(argv[0],'/')) == NULL) {
my_name = argv[0];
} else ++my_name;
while((c = getopt(argc,argv,"h?dc:")) != -1) {
switch(c) {
case 'c' :
{
int ch = atoi(optarg);
if ( ch == 1 ) channel = CHANNEL1;
else if ( ch == 2 ) channel = CHANNEL2;
else {
fprintf(stderr,"No such channel\n");
fprintf(stderr,USAGE,my_name);
exit(1);
}
}
break;
case 'd' :
debug = TRUE;
break;
case '?' :
fprintf(stderr,USAGE,my_name);
exit(1);
}
}
argc -= optind;
argv += optind;
{
fd = init(channel);
for(;;) {
sleep(1);
printf("%s amps: %.2lf\n",getTimeStr(),readAmps(fd,debug));
}
}
exit(0);
} /* End of main() */
/*************************************************************/
const char* getTimeStr()
{
static char time_str[26] = "";
time_t result = time(NULL);
strftime(time_str,sizeof(time_str),"%FT%T",localtime(&result));
return time_str;
} // End of getTimeStr()
/*************************************************************/
double readAmps(int fd,int debug)
{
uint8_t buf[3];
int16_t val;
double rms_val = 0.0;
double amps = 0.0;
wiringPiI2CWrite(fd,0xab);
if (read(fd, buf, 2) != 2) {
perror("Read conversion");
return -1.0;
}
val = (int16_t)buf[0]*256 + (uint16_t)buf[1];
rms_val = (double)val*(5.0/(double)4096);
if ( debug ) printf("%s volts: %.4lf\n",getTimeStr(),rms_val);
amps = 30.0*rms_val; // 30 amps per volt split-core transformer
return amps;
} // End of readAmps()
/*************************************************************/
int init(int channel)
{
int fd = -1;
if (wiringPiSetup() == -1) {
printf("Setup failed\n") ;
exit(1);
}
// MCP3221 A5 | A6
if ((fd = wiringPiI2CSetup(channel)) == -1) {
printf("I2C Setup failed\n") ;
exit(1);
}
return fd;
} // End of init()
/*************************************************************/
/******************** END OF FILE ****************************/
/*************************************************************/
Python Software
First install python-smbus.
python-smbus
Download
#! /usr/bin/python
##########################################################
#
# Acme Software Works
#
##########################################################
##########################################################
#
# Example code for the Acme Software Works RMS RPI MOD
#
##########################################################
import smbus
import time
from time import gmtime, strftime
import datetime
import getopt, sys
CHANNEL1 = 0x4d
CHANNEL2 = 0x4e
def usage():
print "Usage: amps.py [-d (debug)] [-c 1|2 (channel)]\n"
def readAmps(ch,debug):
bus = smbus.SMBus(1)
if ch == "2":
addr = CHANNEL2
else:
addr = CHANNEL1
data = bus.read_i2c_block_data(addr, 1,2)
val = (data[0] << 8) + data[1]
rms_val = float(val)*(5.0/float(4096));
amps = 30.0*rms_val # 30 amps per volt split-core transformer
now = strftime("%Y-%m-%d %H:%M:%S", gmtime())
if debug == True:
print now,"volts:",'{:.4f}'.format(rms_val)
print now," amps:",'{:.2f}'.format(amps)
time.sleep(1)
# main
try:
opts, args = getopt.getopt(sys.argv[1:], "dc:")
except getopt.GetoptError as err:
print str(err)
usage()
sys.exit(2)
ch = "1"
debug = False
for o, a in opts:
if o == "-d":
debug = True
elif o == "-c":
if a in ("1","2"):
ch = a
else:
usage()
sys.exit(2)
else:
assert False, "unhandled option"
while True:
readAmps(ch,debug)
##### End of File ######
For more information: info@acmesoftwareworks.com