Please, somebody has used a SCT-013 current sensor, were you able to get it to work?

Can you help me kindly share your code micropython?

I have problems reading the alternate voltage !

Thanks a lot ]]>

Please, somebody has used a SCT-013 current sensor, were you able to get it to work?

Can you help me kindly share your code micropython?

I have problems reading the alternate voltage !

Thanks a lot ]]>

Hi,

adc = machine.ADC()

apin = adc.channel(pin='P13', attn=ADC.ATTN_11DB)

apin.voltage() = 1.65 setoff

run your script :

2.780501

3.879016

2.182554

4.272707

1.722777

4.401552

1.644289

3.699388

2.480205

3.061597

3.568601

2.0923

3.860344

1.322279

3.831977

3.498566

2.901784

3.49483

1.605297

4.428602

1.789285

3.803602

2.711144

2.608934

4.229746

1.647269

3.845066

2.585204

3.724775

1.578623

3.934014

2.453763

2.625341

3.411754

2.14921

4.005078

2.022429

3.44038

3.395152

2.577068

4.131443

2.113191

4.049322

3.241598

3.14133

3.520792

1.849467

3.521152

1.591729

3.71154

3.290737

2.512628

3.499491

1.519029

3.65791

1.811457

3.005512

3.636473

Handler stopped

can you explain me how i should use these numbers at no load?

Thans a lot

]]>0.02607839

0.01572799

0.01494074

0.05918682

0.03789386

0.05230555

0.008513147

0.03744077

0.02928547

0.02655266

That's just the basic noise. about 0.03/240 or ~0.1% of the full range. Script that created these numbers below:

```
#
import gc
import math
import array
from utime import sleep_ms, sleep_us
from machine import ADC, Timer, idle, enable_irq, disable_irq, Pin, SPI
#
# acquire ADC values. The paramters of the constructor are:
# 1. the mains frequency (number, typically 50 or 60)
# 2. the sampling period (ms). The default is 2 ms.
#
class Acquire:
def __init__(self, freq=50, *, sample_period=2):
self.sample_period = sample_period
self.omega = 2.0 * math.pi * freq / (1000 / sample_period)
self.coeff = 2.0 * math.cos(self.omega)
self.freq = freq
self.adc = ADC(bits=9)
def start(self, pin, time=200):
gc.collect() # avoids automatic gc during sampling
self.pin_adc = self.adc.channel(pin=pin, attn=ADC.ATTN_11DB)
self.samples = time // self.sample_period
# Test code for creating the artificial signal
# pi_f = (2 * math.pi * self.freq * self.sample_period) / 1000
# self.sin_vec=array.array("f", [math.sin(pi_f * _) for _ in range(self.samples)])
self.count = 0
self.busy = True
self.q1 = 0.0
self.q2 = 0.0
self.alarm = Timer.Alarm(self.read_adc, 0, ms=self.sample_period, periodic=True)
def stop(self):
self.alarm.cancel()
def read_adc(self, alarm):
self.q0 = self.pin_adc() + self.coeff * self.q1 - self.q2
# Test code usign an artificial Signal
# self.q0 = self.sin_vec[self.count] + self.coeff * self.q1 - self.q2
self.q2 = self.q1
self.q1 = self.q0
self.count += 1
if self.count >= self.samples:
self.alarm.cancel()
self.busy = False
def result(self):
while self.busy == True:
sleep_ms(self.sample_period)
# amplitude = 2 * math.sqrt(self.q1 * self.q1 +
# self.q2 * self.q2 -
# self.q1 * self.q2 * self.coeff) / self.count
# if phase is required:
real = self.q1 - self.q2 * math.cos(self.omega)
imag = self.q2 * math.sin(self.omega)
amplitude = 2 * math.sqrt(real * real + imag * imag) / self.count
phase = math.atan2(real, imag)
#
return amplitude
def reading(self, pin, time):
self.start(pin, time)
return self.result()
#
def run(period = 1000):
for _ in range(10):
acq = Acquire(50.000) # 50 Hz
value = acq.reading("P13", period)
print (value)
```

]]>Many thanks Robert for you help, I've used your script code the result is:

empty power:

0.09

0.04

0.12

0.03

0.05

0.01

0.02

0.11

0.07

0.11

Now for convert in amper how I do it? it sensor is at 15 A

Thanks a lot

]]>Edit: I repeated the test. Since my power supply is rated for 5A, i made three turns through the sensor, increasing the sensitivity by 3. At 5A, it shows 192. so the correction factor is 1/12.8. Above 12A my sensor goes slowly goes into saturation.

Edit 2:

Mains frequency is usually stabilized to be between 49.8 and 50.2 Hz. Only in very rare conditions it's outside. <50Hz means too much load. >50ZHz means too much power. Below 49.8Hz, first large industrial consumers are dropped step by step. e.g. At 47.5Hz, the power supply is completely shut off. Or at 51.5 Hz, all solar power generators are shut off, ...

Many thanks Robert, I'm use your script code as a library :-),

the period at 500 millisecond I've good result

It also read 6 watts, incredible!!!

Best regards

]]>```
Freq 1 sec 400ms
50 1.0 1.0
50.2 0.883 0.993
49.8 0.985 0.996
```

Two results:

a) the numbers are is not symmetric. That is caused by the internal clock not being exactly in time. You can compensate for that by slightly modifying the argument to the class constructor, using e.g 50.08 (that would by the best value here) instead of 50.

For a assumed frequency of 50.08, I get:

```
Freq 1 sec 400ms
50 1.0 1.0
50.2 0.934 0.996
49.8 0.933 0.996
```

b) Shorter sampling times are less sensible to frequency variations.

Since you mentioned EMonlib a while ago. I looked into that code and saw, that is is almost identical to an approach which I had tested before. That has different pro's and con's. While the emonlib approach is not sensible to frequency variations, it has a large zero current reading. just because it is not frequency selective, it picks up all noise. In my test, the no current noise was equivalent to about 70 mA current. In comparison your zero current noise figures are equivalent to about 4 mA current.

]]>Hi Robert,

is possible know also the voltage AC 230.

Now is a constant 230 volt

Best regards

]]>```
#
import gc
import math
import array
from utime import sleep_ms, sleep_us, ticks_us, ticks_diff
from machine import ADC, Timer, idle, enable_irq, disable_irq, Pin, SPI
#
# acquire ADC values. The paramters of the constructor are:
# 1. the mains frequency (number, typically 50 or 60)
# 2. the sampling period (ms). The default is 2 ms.
#
class Acquire:
def __init__(self, freq=50, *, sample_period=2):
self.sample_period = sample_period
self.omega = 2.0 * math.pi * freq / (1000 / sample_period)
self.coeff = 2.0 * math.cos(self.omega)
self.freq = freq
self.adc = ADC(bits=9)
def start(self, pin, time=200):
gc.collect() # avoids automatic gc during sampling
self.pin_adc = self.adc.channel(pin=pin, attn=ADC.ATTN_11DB)
self.samples = time // self.sample_period
# Test code for creating the artificial signal for phase noise
# self.pi_f = (2 * math.pi * self.freq) / 1000000 # angle step per us
# self.start_us = ticks_us()
self.count = 0
self.busy = True
self.q1 = 0.0
self.q2 = 0.0
self.alarm = Timer.Alarm(self.read_adc, 0, us=(self.sample_period * 1000 - 3), periodic=True)
def stop(self):
self.alarm.cancel()
def read_adc(self, alarm):
self.q0 = self.pin_adc() + self.coeff * self.q1 - self.q2
# Test code usign an artificial Signal
# self.q0 = math.sin(ticks_diff(ticks_us(), self.start_us) * self.pi_f)
self.q2 = self.q1
self.q1 = self.q0
self.count += 1
if self.count >= self.samples:
self.alarm.cancel()
self.busy = False
def result(self):
while self.busy == True:
sleep_ms(self.sample_period)
amplitude = 2 * math.sqrt(self.q1 * self.q1 +
self.q2 * self.q2 -
self.q1 * self.q2 * self.coeff) / self.count
# if phase is required:
# real = self.q1 - self.q2 * math.cos(self.omega)
# imag = self.q2 * math.sin(self.omega)
# amplitude = 2 * math.sqrt(real * real + imag * imag) / self.count
# phase = math.atan2(real, imag)
#
return amplitude
def reading(self, pin, time):
self.start(pin, time)
return self.result()
#
def run(period = 1000, n=10):
acq = Acquire(50.0, sample_period=2) # 50 Hz
sum = 0
for _ in range(n):
value = acq.reading("P13", period)
sum += value
print (value)
print ("average: ", sum/n)
```

]]>Once you have current and voltage with each an amount and a phase relative to the same reference, you can calculate the phase between current and voltage. ]]>

Hi,

thank for support,

into my project there is a transformer 220 to 5 v DC

what is the wiring diagram to measure the voltage for Lopy ?

Thanks a lot

]]>Unregulated - because it has to follow the fluctuations of the 230V mains,

50 Hz AC - because you need the phase information to calculate the power factor cos(phi) between voltage and current. ]]>

I'm sorry, but not have space into my pcb , Can I use stc 013 variations set off 1.65 at empty?

Thanks a lot ]]>

]]>

Hi,

Is there another alternative, just electronic components without transformer?

Example my electronic ammeter?

]]>

As initially said, you may also use an optocoupler. But the transfer characteristic is not linear, it changes with the temperature, and they are not made for analog mode. But still that may do. I can make tests for that and see, how that works.

]]>The signal at P13 is:

The picture shows color grading. The red area is the hot one which the trace most often passes. For the the picture the function generator was used as AC source.

I measured the result with the goertzel test script, which only looks at the 50Hz component of the signal. For a constant input voltage the output readings vary by +/- 1 %.

But the output varies a lot with temperature. Cooling down the optocoupler increased the output by ~30%.

Sample picture below. The pink area is the one the trace swept over during temp variation. The numbers displayed by the goertzel script went from ~120 to ~165.

The Input/output transfer function is not linear. It is about Vout = x * Vin ** 0.7. But for a small range line 230V +/- 10% it's ok.

The blue line is Vin, the red Vout.

The transfer function is as below:

Not too bad, almost linear in the upper range. What stays is the huge temperature dependency.

Because the green and red line are almost identical, here the table:

```
Volt Average Sdev dAvg/dVolt
100 23,56 0,55
125 40,18 0,33 0,6648
150 60,11 0,6 0,7972
175 82,32 0,83 0,8884
200 106 0,778 0,9472
210 115,5 0,68 0,95
220 126,1 0,832 1,06
230 135,3 0,88 0,92
240 145,73 0,73 1,043
250 157 0,549 1,127
260 167,88 0,638 1,088
270 179,28 0,883 1,14
```

]]>Hi Robert,

thanks for you support,

I try this way , please, the componets how many watts and tolerances?

Thanks advance

]]>Note. BE CAREFUL when testing to avoid an electric shock! I used an isolating variable output transformer for my tests to be safe.

Watts: the only part of interest is R6. So you have:

P = 230 * 230 / 390000 0.136 W

So that is negligible. The critical property of R6 is voltage capability. You have to get a 350V RMS type. That's typically better for higher power resistors. So get a 0.5W or 1W type.

The value of the resistor is to be determined. Since the transfer ratio of the optocoupler varies, you have to select a proper value such that at 230 V the output at R7 is about 2Vpp. You need an oscilloscope to tell. If you just have a Multimeter, use 0.7V AC. Below is a modified script capturing data from P13 and P14 (current and voltage). It returns the raw values and the phase difference between the two signals. That phase difference may have an offset, since the voltage signal is only half of the sine wave.

```
#
import gc
import math
import array
from utime import sleep_ms, sleep_us, ticks_us, ticks_diff
from machine import ADC, Timer, idle, enable_irq, disable_irq, Pin, SPI
#
# acquire ADC values. The paramters of the constructor are:
# 1. the mains frequency (number, typically 50 or 60)
# 2. the sampling period (ms). The default is 2 ms.
#
class Acquire:
def __init__(self, freq=50, *, sample_period=2):
self.sample_period = sample_period
self.omega = 2.0 * math.pi * freq / (1000 / sample_period)
self.coeff = 2.0 * math.cos(self.omega)
self.freq = freq
self.adc = ADC(bits=9)
def start(self, pin1, pin2, time=200):
gc.collect() # avoids automatic gc during sampling
self.pin_adc1 = self.adc.channel(pin=pin1, attn=ADC.ATTN_11DB)
self.pin_adc2 = self.adc.channel(pin=pin2, attn=ADC.ATTN_11DB)
self.samples = time // self.sample_period
# Test code for creating the artificial signal for phase noise
# self.pi_f = (2 * math.pi * self.freq) / 1000000 # angle step per us
# self.start_us = ticks_us()
self.count = 0
self.busy = True
self.q1_1 = 0.0
self.q2_1 = 0.0
self.q1_2 = 0.0
self.q2_2 = 0.0
self.alarm = Timer.Alarm(self.read_adc, 0, us=(self.sample_period * 1000 - 3), periodic=True)
def stop(self):
self.alarm.cancel()
def read_adc(self, alarm):
state = disable_irq()
v1 = self.pin_adc1()
v2 = self.pin_adc2()
# Test code using an artificial Signal
# v1 = math.sin(ticks_diff(ticks_us(), self.start_us) * self.pi_f)
# v2 = math.sin(ticks_diff(ticks_us(), self.start_us) * self.pi_f)
enable_irq(state)
self.q0_1 = v1 + self.coeff * self.q1_1 - self.q2_1
self.q2_1 = self.q1_1
self.q1_1 = self.q0_1
self.q0_2 = v2 + self.coeff * self.q1_2 - self.q2_2
self.q2_2 = self.q1_2
self.q1_2 = self.q0_2
self.count += 1
if self.count >= self.samples:
self.alarm.cancel()
self.busy = False
def result(self):
while self.busy == True:
sleep_ms(self.sample_period)
real = self.q1_1 - self.q2_1 * math.cos(self.omega)
imag = self.q2_1 * math.sin(self.omega)
amplitude_1 = 2 * math.sqrt(real * real + imag * imag) / self.count
phase_1 = math.atan2(real, imag)
real = self.q1_2 - self.q2_2 * math.cos(self.omega)
imag = self.q2_2 * math.sin(self.omega)
amplitude_2 = 2 * math.sqrt(real * real + imag * imag) / self.count
phase_2 = math.atan2(real, imag)
#
diff = 40e-6 * 2 * self.freq * math.pi # phase diff caused by 40µs sampling delay
phase = phase_1 - phase_2 - diff #
if phase > math.pi:
phase -= 2 * math.pi
elif phase < -math.pi:
phase += 2 * math.pi
return amplitude_1, amplitude_2, phase
def reading(self, pin1, pin2, time):
self.start(pin1, pin2, time)
return self.result()
#
def run(period = 500, n=10):
acq = Acquire(50.0, sample_period=1) # 50 Hz
l=[]
for _ in range(n):
value = acq.reading("P13", "P14", period)
l.append(value[0])
print (value, math.cos(value[2]))
avg = sum(l)/n
sqsum = 0
for _ in l:
sqsum += (_ - avg) * (_ - avg)
print ("average: ", avg, "SDev:", math.sqrt(sqsum/n))
```

]]>Hello,

I built my electronic circuit, now how should i use the result of the value [2] to get the correct voltage?

value = acq.reading("P13", "P14", 500)

print(value[2])

Thank a lot

]]>Peff = U * I * cos(phi)

with I derived from value[0], U derived from value[1] and phi being value[2], which already has the proper scaling. The number cos(phi) is called the power factor. It is 1 for pure resistive loads, and 0 for pure capacitive or inductive loads. Values close to 1 are common, values close to 0 are rare.

]]>Dear Robert,

The reading voltage with external voltmeter it's:

At 230 v and U from value[1] it's from 4

At 220 v and U from value[1] it's from 2

How can I find the right formula?

Thanks a lot

]]>You can either reduce the value of the 390k resistor R6, or increase the value of the 10k resistor R7. You may also try to remove C4, because that one also reduces sensitivity. When the setting is right, you should measure with an multimeter an AC voltage of about 0.7 V~.

The transfer ratio of optocouplors vary a lot. ]]>

I'm so sorry, I thought you did the tests with the resistances, I don't have the equipment or material to try.

Thanks you

]]>