Advice regarding CT and mains voltage monitoring project

Hi everyone!

Long story short, I'm in the process of building my own mains current and voltage monitoring project.

For the current monitoring, I found this design: http://www.rotwang.co.uk/images/mains_current.gif from http://www.rotwang.co.uk/projects/energy_monitor.html (using an ADS1115 instead of the MCP6S91,) and for the voltage monitoring, there's the openEnergyMonitor page: http://openenergymonitor.org/emon/node/58, all connected to an Arduino Uno.

My question is - I know that in order to not totally fry my Arduino, I need to offset the AC waveform so that the bottom of the waveform is still a positive voltage. This will be the case for both the current and voltage monitoring.

What I'm wondering is - how should I handle things if my 5v rail should go offline for some reason? (arduino off, etc.) Will this affect the arduino (or the ADS1115) negatively if both are unpowered? Or, would it be best to add some sort of solid state relay, such as the LAA110, into the mix, so that the AC side can be isolated from the Arduino and ADS1115 when the DC power is removed?

Thanks for letting me pick your brains!

chrismyers81: My question is - I know that in order to not totally fry my Arduino, I need to offset the AC waveform so that the bottom of the waveform is still a positive voltage. This will be the case for both the current and voltage monitoring.

From looking at the schematics on the site you linked, all that stuff has been taken care of. Are you modifying the design, or designing your own circuit?

Note - this is not an endorsement. The design looks pretty cheesy. My bar is automatically raised much higher when I see something connected to the mains supply.

What I'm wondering is - how should I handle things if my 5v rail should go offline for some reason? (arduino off, etc.)

Put a 10K resistor in series with the ADC input pin. This will prevent excessive current flow if the Arduino is unpowered, but the input circuit is powered.

What accuracy do you expect? ADS1115 is slow, you need something with 2.4 ksps (60Hz) or PGA as in original design

Hi!

I'm sorry for the delayed response back, it's been a crazy day. Finally have a few minutes free before it's time to head off in another direction... Definitely appreciate the feedback so far!

I hadn't intended to modify the original project I'd found beyond changing to potentially using the ADS1115 (and probably throwing a 5v1 zener on the pins of the ADS1115 to protect it - would that be a good idea with a CT?)

Personally, I do want accuracy, but don't need to know 100% sure that I'm using 29.54 Amps (or whatever.) Out of curiosity, how accurate can I be using an ADS1115? For the most part, I'm hoping to be accurate to around an amp if possible. I'd planned on doing 100 reads of each leg (alternating between the two) as fast as it would let me do the reads, and averaging each leg out. And then doing that polling about every 30 seconds or so. I don't know how accurate it would be overall, but thought that it looked better than most of the other designs that I saw online that basically just did a voltage divider, capacitor, and offset circuit. This at least had some (minimal) extra circuitry for protection. Should I maybe throw in a smoothing capacitor if I'm not reading it at a super high rate?

The reason that I went this route instead is because I don't have any extra digital pins on my Uno. I've got 15 analog sensors to read (in addition to the mains current and voltage,) and also have 7 relays to control as a part of the project, so I had to use up all of my digital pins with an analog multiplexer and relay controller. So, right now I think I have one digital pin free, and the two digital pins that you have to use for serial :/ So, since the MCP6S91 uses SPI, which is tied to the digital pins, I had to rule it out :/ Would there be a more ideal way of going about this? (Space is at a premium in the project, otherwise I'd just get a Mega and be done with it.)

But with all of that said - I'm definitely up for suggestions on how to improve this, because most of what I'm planning on doing with the CTs is based off of things I've found via Google.

For reference, I have two Magnelab SCT-0750-150's, which are rated at .25v at 150 amp, and supposedly have a built-in burden resistor.

...would some sort of peak-hold circuit help?

http://www.planetanalog.com/author.asp?section_id=396&doc_id=562072

chrismyers81: ...would some sort of peak-hold circuit help?

http://www.planetanalog.com/author.asp?section_id=396&doc_id=562072

No. For estimation may be, like indicator "load on" "load off" - not measuring device.

There are a lot of confusion I see. You have to set a priority list, what is more important, what is less. Don't mean to be a mentor, but it isn't an excuse "i don't have a pin, right resistor or good ADC". If there is no resources, space , board etc, don't measure at all, put dummy blinking led or LCD and show random lighting effects.

IMHO, accuracy should be starting point (let say 0.5%), than if other factors - price space availability are not match, you degrade your initial goal (1%? - 3%?) and re-design.

Start from avr465, very little resource demanding and low costs design.

Not enough pins - put port extender.

I saw online that basically just did a voltage divider, capacitor, and offset circuit.

More than enough to make arduino ADC happy. You don't need precise reference DC voltage for AC measurements. In first post I've seen TL072 - ditch it, two resistor o'k.

I hadn't intended to modify the original project I'd found beyond changing to potentially using the ADS1115 (and probably throwing a 5v1 zener on the pins of the ADS1115 to protect it - would that be a good idea with a CT?)

Bad idea. Follow reply #2, put a resistor in series with adc input. Data sheet ads1115 says 10 mA input current at max, so select right value of resistor. Don't use a zener leakage current 'd ruin your objective of accuracy.

Out of curiosity, how accurate can I be using an ADS1115? For the most part, I'm hoping to be accurate to around an amp if possible. I'd planned on doing 100 reads of each leg (alternating between the two) as fast as it would let me do the reads, and averaging each leg out. And then doing that polling about every 30 seconds or so. I don't know how accurate it would be overall,

Poling ones in 30 seconds? So you designing an indicator? To measure , sampling must be continuous. Interrupt driven - exact in timing.

Now sampling rate . Industry standard (Can't find, probably IEC1036.) prescribe up to 20-th harmonics, 60 x 20 = 1200 and x2 for Nyquist = 2400 minimum sampling rate.

You should understand, that accuracy depends on 3 factors: 1. continuous fast sampling; 2. resolution of the ADC + PGA 3. software - processing data. Having 16 or even 24-bits ADC w/o accounting other 2 factors - doesn't make any sense. Locate AN578 application note by AD. It explains why do you need 17-bits resolution , fast ADC. Slow 16-bits ADC provides less accuracy than built-in 10-bits Arduino's with PGA (AVR465).

Out of curiosity, how accurate can I be using an ADS1115?

If you do sampling exact in timing, 860 SPS by ADC1115 gives up to 6-7 harmonics, so accuracy would depend on AC current content. For resistive load, you can get 0.1% I think, but modern switching power supply as a load, deteriorate this value to 1-3% or even less.

I'd planned on doing 100 reads of each leg (alternating between the two) as fast as it would let me do the reads, and averaging each leg out.

You mean averaging squared samples? This is number 3, software, most important part.

Yep, you could say some confusion :P I like to do a ton of research on everything that I do, and sometimes it conflicts with each other, or advice that I read in one place isn't necessarily ideal (at least to my situation/project.) But since I'm still pretty much a noob, I try to pull it all in, filter it, and figure out what's best... AND love to learn and expand my knowledge at the same time :)

Thanks for the advice about the design of the circuit for the CT circuitry, and eliminating the zener in lieu of the resistor.

For the polling cycle - it is, and isn't, an indicator. Basically trying to just get an idea of the then-current demands of our power at home. Based on your feedback, I may look at adding a second board (nano/micro/etc) to just be dedicated to the power monitoring, and connect the two Arduinos together somehow so that the controller arduino can feed off of the monitoring one.

Thanks for giving me some additional details to consider, and helping me to learn as I go along. That is, after all, the ultimate primary objective in all of my tinkering :)

So…out of curiosity, I’m having some trouble getting the MCP6S91 to work. I verified that my CT sensors are putting out somewhere around 50-80mV (depending on the load in the house obviously…) But, I can’t get the MCP6S91s to actually amplify the voltage.

I think that the problem might be just timing issues with the SPI bus, but have no way to verify it (no oscilloscope, etc.)

I’ve been doing a lot of googling, but so far haven’t been able to figure it out. Based on the datasheet, I can’t really tell if it has a maximum clock frequency, whether it uses MSB or LSB first, etc. I’ve tried all of these different settings, but it never would actually change the amplification setting beyond the default of x1.

Some resources I’ve been referencing trying this out:
http://www.microchip.com/forums/m703484.aspx#703550
http://www.microchip.com/forums/m431434.aspx?high=mcp6s91
http://mark.mckillen.com/2012/12/isolated-mains-power-monitoring-arduino/
http://www.dorkbotpdx.org/blog/paul/spi_transactions_in_arduino

https://www.arduino.cc/en/Reference/SPISettings

https://www.arduino.cc/en/Reference/SPITransfer

I’m using an Arduino Pro Mini 5v/16MHz:
https://www.arduino.cc/en/Main/ArduinoBoardProMini
http://www.pighixxx.com/test/wp-content/uploads/2014/11/promini.png

Is there something that I’m missing, or something that I can try to see if the MCP is getting the correct data, etc.? This is the code I’m using currently:

#include <SPI.h>


#define PGA_CS_PIN_A 10
#define PGA_CS_PIN_B 11

// channel select for SPI ADC (volts)
//#define V_MAINS 0
//#define V_REF 1

// channel select for CT (current)
#define I_MAINS_A 1 //Analog pin 1
#define I_REF_A 0   //Analog pin 0
#define I_MAINS_B 3 //Analog pin 3
#define I_REF_B 2   //Analog pin 2
#define I_GAIN 16    //1, 2, 4, 8, 16, 32

// set up the speed, data order and data mode
//Clock polarity (CPOL) 0, Clock Edge (CKE) 1
SPISettings spi_settings(16000000, MSBFIRST, SPI_MODE1);

//Sensor values
volatile int curr_a;
volatile int ref_a;
volatile int curr_b;
volatile int ref_b;
volatile int voltage;



void setup() {
  Serial.begin(9600);
  Serial.println("Setting up...");

  // PGA CS
  pinMode(PGA_CS_PIN_A, OUTPUT);
  digitalWrite(PGA_CS_PIN_A, HIGH);
  pinMode(PGA_CS_PIN_B, OUTPUT);
  digitalWrite(PGA_CS_PIN_B, HIGH);

  SPI.begin(); //Start the SPI interface
  //SPI.setBitOrder(MSBFIRST);
  //Clock polarity (CPOL) 0, Clock Edge (CKE) 1
  //SPI.setDataMode(SPI_MODE1);

  int gain_code = 0;
  switch(I_GAIN) {
    case 1:  gain_code = 0;  break;
    case 2:  gain_code = 1;  break;
    case 4:  gain_code = 2;  break;
    case 8:  gain_code = 4;  break;
    case 16: gain_code = 6;  break;
    case 32: gain_code = 7;  break;
  }
  Serial.print("Gain: ");
  Serial.println(gain_code);

  SPI.beginTransaction(spi_settings);
  digitalWrite(PGA_CS_PIN_A, LOW);
  SPI.transfer(0x41); //Command for channel select
  SPI.transfer(0);    //Channel 0 (not really important since there's only one channel...)
  SPI.transfer(0x40); //Command for gain
  SPI.transfer(gain_code);
  digitalWrite(PGA_CS_PIN_A, HIGH);
  SPI.endTransaction();

  SPI.beginTransaction(spi_settings);
  digitalWrite(PGA_CS_PIN_B, LOW);
  SPI.transfer(0x41); //Command for channel select
  SPI.transfer(0);    //Channel 0 (not really important since there's only one channel...)
  SPI.transfer(0x40); //Command for gain
  SPI.transfer(gain_code);
  digitalWrite(PGA_CS_PIN_B, HIGH);
  SPI.endTransaction();
}







void loop() {
  ref_a  = readAnalog(I_REF_A);
  curr_a = readAnalog(I_MAINS_A);
  ref_b  = readAnalog(I_REF_B);
  curr_b = readAnalog(I_MAINS_B);

  curr_a = ((curr_a - ref_a));// / I_GAIN);
  curr_b = ((curr_b - ref_b));// / I_GAIN);

  Serial.println("+--+");
      
  Serial.print("Current A: ");
  Serial.println(curr_a);

  Serial.print("RefV A: ");
  Serial.println(ref_a);
      
  Serial.print("Current B: ");
  Serial.println(curr_b);

  Serial.print("RefV B: ");
  Serial.println(ref_b);
}






float readAnalog(int sensorNum) {
  //We poll twice to help offset the charge delay.
  //http://forums.adafruit.com/viewtopic.php?f=25&t=11597
  analogRead(sensorNum);
  float sumOfReadings;
  int numReadings = 0;
  delay(10);
  
  //Take the average of 100 readings  
  for (int loops = 0; loops <= 100; loops++) {
    sumOfReadings += analogRead(sensorNum);
    numReadings++;
  }
  
  float result = (sumOfReadings / numReadings);
  return result;
}