Raspberry pi zero/ Current/Energy monitor issues

Hello Everyone,

I am new to this forum and Raspberry pi. Our team is attempting to build an IOT Energy sensor/monitor utilizing a raspberry pi zero and a non-invasive ac current monitor. Has anyone done this before? Our raspberry pi does not have an audio jack, so we would like to hardwire the sensor/clamp into the board. Any insight would be greatly appreciated.

Cheers!

Ryan,

There is a huge amount of insight on this site, it’s mainly documented in the Resources section. I’d suggest you take a look at that first and then if you have any specific questions come back on the forum.

To answer your specific question on the pi zero and a non-invasive ac current monitor, assuming you mean a current clamp as used with the emonTx then the answer is yes but you’ll need an ADC device as well. I think some people have done this with an ADC hat for the zero.

Dig around and I’m sure a) you’ll learn a lot and b) you might even find what you are looking for.

Simon

As Simon says, it can’t be done directly using only a current transformer and a Raspberry Pi, because the Pi does not have an analogue input. You need to add a means of converting the analogue output of a c.t. into digital form that the Pi can handle - that’s what the ADC device does. When you’ve done that, then if you can’t find what you need in Learn (for the basic principles) or Resources (for actual designs of units in production), then ask.

I have done raspberry pi with non-invasive current clamp sensor, using mcp3008 a/d 10bit, added a 4 channel level shifter to allow the mcp3008 to run at 5.0v, while feeding back to raspberry pi spi communications at 3.3v, this allow the Mcp3008 chip to do 200,000 samples per second. It also needed a voltage divider to shift up the analog reading to all positive values so that it can be fed meaningfully into mcp3008.
Once I installed the lib from adafruit mcp3008, the pi immediately able to read the analog signal, but the voltage from the non-invasive current transducer will be fluctuating, and that is how it is for ac power, to get something meanful, we calculate current by using rms, by summing up each reading from mcp3008 at about 6000 counts per second, then squared each value, sum them all up, then divide total number of samples, and finally, take the square root, all these are one within a split second, and I watched my raspberry CPU went to 40% on one of the 4 CPUs during the process. I am using the act-130-30 30amp at my furnace blower fan, my Chinese clamp meter read 1.95 to 1.98 amp, my raspberry pi read about 1.96 to 1.97amp, I have since then output the reading to a text file and save to sd card as log, it can also catch inrush current, when my heat pump runs, I saw current draw about 3 times higher for moment, then staplized at about 17.8amp per leg, works pretty well

Sounds pretty good, would you care to share your code?

here is my code,
i took the adafruit_python_MCP3008 simpletest.py
and added the emonlib irms calculation function,
adjusted the ical value, it gives me an accurate AC current,
for some reason, i copied and pasted my python code here, and it seems like formatted to something different than my python, any other suggestion how i can share my raw python code?

# Simple example of reading the MCP3008 analog input channels and printing
# them all out.
# Author: Tony DiCola
# License: Public Domain
from __future__ import print_function
from datetime import datetime
import time
import math
import datetime
import RPi.GPIO as GPIO, time, os
import urllib2

# Import SPI library (for hardware SPI) and MCP3008 library.
import Adafruit_GPIO.SPI as SPI
import Adafruit_MCP3008
##=========================================================================================================
#paramters here
ct1_offset   = 0.0
max_voltage  = 600.0      # voltage sensor is 600A max
adc_samples  = 6000
ref_voltage  = 605        ## 3300=3.3v 5000=5.0v
ref_ical     = 163.0
ref_sampleI  = 512
number_of_ct = 2
##=========================================================================================================
# Hardware SPI configuration:
SPI_PORT   = 0
SPI_DEVICE = 0
mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE))

## hardware SPI mapping
## MCP3008 VDD to Raspberry Pi 3.3V
## MCP3008 VREF to Raspberry Pi 3.3V
## MCP3008 AGND to Raspberry Pi GND
## MCP3008 DGND to Raspberry Pi GND
## MCP3008 CLK to Raspberry Pi SCLK
## MCP3008 DOUT to Raspberry Pi MISO
## MCP3008 DIN to Raspberry Pi MOSI
## MCP3008 CS/SHDN to Raspberry Pi CE0

print('Reading MCP3008 values, press Ctrl-C to quit...')
# Print nice channel column headers.
print('| {0:>4} | {1:>4} | {2:>4} | {3:>4} | {4:>4} | {5:>4} | {6:>4} | {7:>4} |'.format(*range(8)))
print('-' * 57)

values = [0]*8
ct     = [0]*8
array  = [0]*adc_samples
rms    = [0]*8
##=========================================================================================================
## RMS calculation function
def CalcIrms(cts):
    NUMBER_OF_SAMPLES = adc_samples
    SUPPLYVOLTAGE = ref_voltage ## 3300=3.3v 5000=5.0v
    ICAL = ref_ical
    sumI = 0
    sampleI = ref_sampleI
    filteredI = 0

for n in range (0, NUMBER_OF_SAMPLES):
   
    lastSampleI = sampleI
    ## sampleI = readadc(cts, SPICLK, SPIMOSI, SPIMISO, SPICS)
	values[i] = mcp.read_adc(i)
	sampleI = values[i]
   	array[n] = sampleI
	sampleI=values[i]
   	## for debug only, print all 6000 values read from MCP3008 ch0
    if i == 0:
	     print(sampleI, end='') 
         print(' ',end='')
    ## end of debug
    lastFilteredI = filteredI
	filteredI = 0.996*(lastFilteredI+sampleI-lastSampleI)
    sqI = filteredI * filteredI
    sumI += sqI           
    sampleI_old = sampleI    
   
I_RATIO = ICAL * ((SUPPLYVOLTAGE/1000.0) / 1023.0)
Irms = I_RATIO * math.sqrt(sumI / NUMBER_OF_SAMPLES)
sumI = 0
rms[i] = round((Irms),2)
## print a new separation line
print(' ')
return Irms
## end of RMS calculation
##=========================================================================================================
	
	
# mcp3008 input
ct1_adc = 0
ct2_adc = 1
ct3_adc = 2
ct4_adc = 3


# Main program loop.
while True:
# Read all the ADC channel values in a list.

for i in range(number_of_ct):
    # The read_adc function will get the value of the specified channel (0-7).
    ct[i] = round(CalcIrms(values[i]),2)
# Print the ADC values.
#------------------------------------------------------------------------------------------------------
print('Raw | {0:>4} | {1:>4} |'.format(*values), end='')
print('AMP | {0:>4} | {1:>4} |'.format(*rms))
##----------------------------------------------------------------------------------------------------------------
##output to file for loggging
##filename  = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
filename  = datetime.datetime.now().strftime("%Y%m%d")
f=open("current_%s.txt" % filename, "a+")
timestamp = datetime.datetime.now().strftime("%A, %d. %B %Y %H:%M:%S %f")
f.write(timestamp)
f.write(' {0:>4} | {1:>4}|'.format(*values)) 
f.write(' {0:>4} | {1:>4}|\n'.format(*rms)) 
f.close   
##----------------------------------------------------------------------------------------------------------------
time.sleep(5)

Formatted code for readability. BT - Moderator

1 Like

Thanks @Bm2016 for sharing that.

I have some mcp3208’s somewhere that I’d like to try one day. I couldn’t see a sample frequency published for 3.3v, only for 2.7v(75KHz) and 5v(200KHz). I was wary of running them at 5v as the increased burden values increase the phase error, but looking at the spec sheet they seem to have a separate Vref so it is possible to use 5v to get the sampling frequency whilst using a lower Vref (3.3v or even less) to avoid swings in the CT phase error. That of course isn’t an issue for you if you are just measuring current, not real power.

1 Like

Hi, Paul

I would like to clarify my python code a little bit more, in particular the parameter section

ref_voltage = 5000 ## 3300=3.3v 5000=5.0v
ref_ical = 30

the ref_voltage is the voltage supplied to MCP3008 pin15, multiplied by 1000
ref_ical would be the rating of the clamp sensor, in my case, it would be 30, because i have the the SCT-013-30, 30amp current sensor,

the MCP3208 is the higher resolution version of MCP3008, i think both of them will work
i have a voltage sensor to measure the AC voltage, together, voltage X current will gives me Watt.
the MCP chip can run either on 3.3v or 5v with bi-directional level shifter. whether at 75k samples per second or 200k samples per second is far beyond my 6000 samples per seconds need, the rms took care of the swing in ct, i am not sure the impact or bottleneck on raspberry SPI, since i am reading the AC current every 5 seconds interval, it seem to be stable and accurate, I have two raspberry pi 3, each is wired to one 30 amp CT, one running MCP3008 at 3.3v, one with 5V and level shifter, i am not able to tell any performance or accuracy difference

I thought you were running at 5v just to get the 200ksps. If you are only sampling at 6ksps why are you running at 5v? 3.3v would have been fast enough and avoided using levelshifters,

The operating voltage and reference voltage are different things. For example you are using a 30A CT from YDHC that outputs 1v, so if you currently have vref at 5v you are only using ~20% of the ADC (at full 30A load) making the ADC operate at less than 8bits effectively.

At 3.3V you would be using just over 30% of the ADC or effectively 8.5’ish bits.

Over on the STM32 development thread we have been discussing the possibility of using a 2.0v Vref, that’s as low as the STM will allow, but the mcp’s seem to allow down to 0.25v so a 1.0v Vref to make full use of the ADC’s resolution (with a 1.0v CT) might be possible.

That can only give you apparent power, which will be fairly accurate when measuring a purely resistive load such as a kettle or immersion heater etc. To measure real power you need a more complex calculation and the timing of the voltage signal compared to the current signal becomes very important, hence the concern about phase error.

I was referring to phase error, that is not related to the RMS. The lower the burden value the less the waveform shifts and the more linear the shift across the range of the CT. Normally (with a sct-013-000 100A CT) you would select a burden to give you full use of the adc at the capacity of the CT (eg 33R at 5v or 22R at 3.3v) the lower the value, the more linear the phase error and therefore easier to correct. Since you are using voltage output CT’s, the burden is already selected and it’s probably pretty low to give a 1.0v output.

See the learn section for more info on calculating real power and correcting phase error.
[Edit - RW] … and to see how the phase error changes with burden value.

[quote=“Bm2016, post:9, topic:6560”]

I used your code for my project to read the values but, I read random values like, for example:

Raw | 707 | 0 |AMP | 0.15 | 0 |

Raw | 411 | 0 |AMP | 0.1 | 0 |

Raw | 373 | 0 |AMP | 0.09 | 0 |

Raw | 710 | 0 |AMP | 0.17 | 0 |

Raw | 526 | 0 |AMP | 0.01 | 0 |

Raw | 302 | 0 |AMP | 0.16 | 0 |

Raw | 670 | 0 |AMP | 0.15 | 0 |

Raw | 714 | 0 |AMP | 0.16 | 0 |

Raw | 302 | 0 |AMP | 0.16 | 0 |

What’s happening?

my code:

# Simple example of reading the MCP3008 analog input channels and printing
# them all out.
# Author: Tony DiCola
# License: Public Domain
from __future__ import print_function
from datetime import datetime
import time
import math
import datetime
import RPi.GPIO as GPIO, time, os
import urllib2

# Import SPI library (for hardware SPI) and MCP3008 library.
import Adafruit_GPIO.SPI as SPI
import Adafruit_MCP3008
##=========================================================================================================
#paramters here
ct1_offset   = 0.0
max_voltage  = 600.0      # voltage sensor is 600A max
adc_samples  = 6000
ref_voltage  = 3300        ## 3300=3.3v 5000=5.0v
ref_ical     = 20
ref_sampleI  = 512
number_of_ct = 1
##=========================================================================================================
# Hardware SPI configuration:
SPI_PORT   = 0
SPI_DEVICE = 0
mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE))

## hardware SPI mapping
## MCP3008 VDD to Raspberry Pi 3.3V
## MCP3008 VREF to Raspberry Pi 3.3V
## MCP3008 AGND to Raspberry Pi GND
## MCP3008 DGND to Raspberry Pi GND
## MCP3008 CLK to Raspberry Pi SCLK
## MCP3008 DOUT to Raspberry Pi MISO
## MCP3008 DIN to Raspberry Pi MOSI
## MCP3008 CS/SHDN to Raspberry Pi CE0

print('Reading MCP3008 values, press Ctrl-C to quit...')
# Print nice channel column headers.
print('| {0:>4} | {1:>4} | {2:>4} | {3:>4} | {4:>4} | {5:>4} | {6:>4} | {7:>4} |'.format(*range(8)))
print('-' * 57)

values = [0]*8
ct     = [0]*8
array  = [0]*adc_samples
rms    = [0]*8
##=========================================================================================================
## RMS calculation function
def CalcIrms(ct):
    NUMBER_OF_SAMPLES = adc_samples
    SUPPLYVOLTAGE = ref_voltage ## 3300=3.3v 5000=5.0v
    ICAL = ref_ical
    sumI = 0
    sampleI = ref_sampleI
    filteredI = 0

    for n in range (0, NUMBER_OF_SAMPLES):
   
        lastSampleI = sampleI
    
        values[i] = mcp.read_adc(0)
        sampleI = values[i]
        array[n] = sampleI
        sampleI=values[i]
 
        lastFilteredI = filteredI
        filteredI = 0.996*(lastFilteredI+sampleI-lastSampleI)
        sqI = filteredI * filteredI
        sumI += sqI           
        sampleI_old = sampleI    
   
        I_RATIO = ICAL * ((SUPPLYVOLTAGE/1000.0) / 1023.0)
        Irms = I_RATIO * math.sqrt(sumI / NUMBER_OF_SAMPLES)
        sumI = 0
        rms[i] = round((Irms),2)
    print(' ')
    return Irms
## end of RMS calculation
##=========================================================================================================
	
	
# mcp3008 input
ct1_adc = 0
ct2_adc = 1
ct3_adc = 2
ct4_adc = 3


# Main program loop.
while True:
# Read all the ADC channel values in a list.

    for i in range(number_of_ct):
    # The read_adc function will get the value of the specified channel (0-7).
        ct[0] = round(CalcIrms(values[i]),2)
# Print the ADC values.
#------------------------------------------------------------------------------------------------------
    print('Raw | {0:>4} | {1:>4} |'.format(*values), end='')
    print('AMP | {0:>4} | {1:>4} |'.format(*rms))

time.sleep(5)

Could you help-me?

i need only ch01 read of mcp3008

assume your hardware circuit is correct, if there is any AC current flowing through the Current sensor, the Raw number is an instantaneous reading of current at your interval of every 5 seconds, suppose to be fluctuating, like a sine wave, higher the current, bigger the fluctuation.

the rms (root mean square) of 6000 samples will give you AC current in amps,

did you build your circuit from breadboard, or soldered into a wafer board?
i learned a hard lesson when soldering, forgot to clean up the flux paste, in my case it make a huge different because i am using a DC biasing circuit to up-shift the voltage to positive, after i use q-tips and rubbing alcohol to clean out the every flux paste, the circuit has been working beautifully.

if there is no current floating, the raw reading should be steady at 512, will translate to nearly 0 amps.

i highly suspect problem maybe your circuit has been mis-wired.

i am using the dc-bias circuit with four 10k ohm resistors and two 10 uF capacitors similar to the last picture on the following link.

http://www.falstad.com/circuit/circuitjs.html?cct=$+1+0.000005+51.8012824668342+50+5+50 r+288+64+288+160+0+10000 c+288+64+432+64+0+0.00001+-2.4789972859207126 r+432+64+432+160+0+10000 R+288+64+240+64+0+1+40+2+0+0+0.5 O+432+64+480+64+0 g+288+160+288+176+0 r+352+240+352+304+0+10000 r+352+304+352+368+0+10000 R+352+240+352+208+0+0+40+5+0+0+0.5 g+352+368+352+384+0 c+400+304+400+368+0+0.00001+2.5080583286223868 w+352+304+400+304+0 w+352+368+400+368+0 w+400+304+432+304+0 w+432+160+432+304+0 x+442+310+492+313+4+20+Vbias x+176+70+219+73+4+20+input o+3+64+0+4098+2.187250724783012+0.00034175792574734563+0+2+3+3 o+4+64+0+4098+8.749002899132048+4.8828125e-105+1+1

you have

ct[0] = round(CalcIrms(values[i]),2)

i have

ct[i] =round(CalcIrms(values[i]),2)

Indeed. That means that the result for every c.t. will be put into ct[0], therefore when you print the value, you will print the last value that was put into ct[0].

Hi, Robert

I was using the SCT-013-020, SCT-013-030, SCT-013-060, SCT-013-100, the blue color one from ebay, and output 1V, the python work fine

then i also tried the Magelab SCT-0750-150, output 0.333V instead 1V, and when i tried to turn on the heatgun, handheld clamp amp meter read 10.6amp, but python read 3.5 to 3.6amp, if i were to multiple the python reading by 3, then i will get the correct reading, is the op amp best way to amplify output voltage to get more accurate reading?

Look at ‘Learn’ and the derivation of the calibration coefficients. I know it is rather complicated, but that explains why your reading is wrong.

You can amplify the input voltage, or you might be able to reduce the analogue reference that your ADC uses, and thereby increase the sensitivity of the input.

This is the reason why we don’t encourage the use of voltage output c.t’s, and especially 0.333 V output c.t’s.

But of course, it is always best to size the c.t. to suit the load you are measuring.

Hi, Robert

as you suggested,
i played around with my dc biasing voltage divider a little bit, and added two resistors for the analog ref voltage of MCP3008, reduce voltage to 1v instead of 3.3v, the divider voltage became 500mV, and also change the python code in raspberry pi, instead of amplify the Magnelab SCT-0750-200 output voltage from 0.333v to 1v with rail to rail op amp.

ref_voltage = 1000 ## 3300=3.3v 5000=5.0v
ref_ical = 200
ref_sampleI = 512

now i have a nice and accurate amp full resolution reading from raspberry pi
when the house appliance like the fridge, fans, tv turn on and off periodically, even the led lights, i do see the the reading swings up and down according.

Hi,

I am using your code but i am not getting any proper reading while using with MCP3008. I am using Raspberry pi zero w, Is it capable of 6000 samples ?

anyone tried with Raspberry pi zero ? also can you explain what should i enter in Ref_ical = ? i am using YHDC SCT-013-030 1V output for 30A.

Can you explain what you mean by

To get the answer to

you must study the data sheet for your MCP3008 and work out what the calibration will be for your c.t. You know the c.t. produces 1 V rms for 30 A rms input, and you know the MCP3008 converts voltage over a range 0 - VREF to numbers over the range 0 - 1023, so you must calculate using those values.