Bu yeni yaptığım birşey değil; daha önce de veri kaydedici bir cihaz alıp kodlamıştım ama bu sefer -nispeten- daha iyi bir veri kaydetme yazılımı-donanımı gerçekleştirdim.
Öncelikle belirteyim; ana donanımımız Raspberry Pi Zero W
Bu cihazı şuanda yaklaşık 80TL’ye alabilirsiniz. Pi Zero’lar kendi içerisinde üçe ayrılır; bir modelinde wireless yok. Wireless olmayan cihaz 40TL civarında ama evde wireless kullanacağım için ben tercih etmedim. Wireless yerine ethernet kullanacaksanız bir USB->Ethernet dönüştürücü de almanız gerekli.
Ethernet çeviricilerin fiyatları yaklaşık 15TL’den başlıyor ama eğer ciddi bir iş için kullanacaksanız buna uygun daha kaliteli bir USB-Ethernet cihazı kullanmanızı öneririm.
Şimdi gelelim akım/gerilim ölçümü yapacağımız analizöre. Ben evde analizör olarak Socomec Diris A10 kullanıyorum. Türk malı Entes gibi ürünler de kullanabilirsiniz. Ben elimde olduğu için kullandım.
Bu cihaz RS485 Modbus üzerinden veri verebilen bir analizör. En basit ve bana gerekli verileri sunabilen cihazlardan.
Şimdi RS485 nedir Modbus nedir diyenler için kısaca açıklayayım. USB (Universal Serial Bus) çıkmadan evvel zaman önce (burada yaşımı açık ediyorum) cihazların haberleşmesi için seri ve paralel portlar vardı.
Bu fotoğrafta soldaki port evvel zaman içerisinde kullanılan RS232 portu sağdaki da yine evvel zamanda yazıcıları bağlamak için kullanılan paralel port. Bu portları artık günümüzde bulmanız mümkün değil ama endüstride seri portun bir gelişmişi olan RS485 hala aktif olarak kullanılıyor.
RS232 iki cihazın birbiri ile haberleşmesi için tasarlanmış bir ürün. Daha sonra demişler ki neden birden fazla cihaz haberleşemesin? İşte o açığı da RS485 kapatmış. RS485 haberleşme hattı üzerinden tek bir cihazdan birçok cihaza erişebilirsiniz. Raspberry üzerinde RS485 ile gelmiyor o sebeple USB’den bir RS485 dönüştürücü almanız gerekli.
Yukarıdaki ürün piyasada 10TL-15TL arasında bulabileceğiniz bir ürün. Evde hobi işleri yapmak için işinizi görecektir. Eğer endüstriyel bir ürün yapmak istiyorsanız bu iş için endüstriyel olarak tasarlanmış bir Raspberry kartına ihtiyacınız var. Bu aşamada Türkiye’de Solarify firması epey yol katetmiş durumda. Raspberry Pi tabanlı kendi datalogger cihazlarını ürettiler.
Bunlar tabi endüstriyel ürünler. Eğer hobi için uğraşıyorsanız benim gibi bu ürünleri kullanmanıza gerek yok.
Unutmadan paylaşayım; Diris A10’un akım okuyabilmesi için akım trafosuna ihtiyaç var. Birtane de akım trafosu almanız gerekli. Akım trafolarını da 20TL-70TL bandında bulabilirsiniz. Tek ürün alacağınız için bu ürünlerde biraz “tutturabildiğine” piyasası hakim.
Şimdi gelelim sistemin kurulumuna. Öncelikle akım taşıyan kabloları akım trafosunun içerisinden geçirmeniz gerekiyor. Bu işlemi yapmak için evinize gelen elektriği binanın panosundan kesin. Sonra evinizin içindeki panoyu düzenlersiniz.
Peki ben nasıl yaptım?
Önce tüm sistemi eve giriş panosunun içine koymaya çalıştım ama pano yetmedi. O sebeple sadece akım trafosunu panonun içerisine yerleştirdim. Bizim ev tek faz (monofaze) olduğu için tek akım trafosu ile işi çözdüm ama sizin eviniz üç fazlı (trifaze) ise üç adet akım trafosu kullanmanız gerekli.
Geriye kaldı Diris A10 ve Raspberry. Onları nereye koyacaktım? İlk seferinde panoya sığmadığı için hayalkırılkığı yaşasam da daha sonra alçıpan boşluğunu keşfettim. Sonra dedim ki gerilim ve akım trafosunun kablosunu alçıpanın içine koyarsam bu iş olur ve hummalı çalışmalara başladım.
Alçıpanda bir delik açıp cihazları onun içine yerleştirdim. Panodan çıkan kabloları da kablo kanalına koydum. (Birinci şahıs konuşuyorum ama ben genelde elektrik tesisatına pek dokunan birisi değilim o sebeple elektrikçiye yaptırdım).
İçeride şöyle bir sistem oldu
Ve sonuçta ortaya böyle amatörce birşey çıktı. Burada önemli olan kısım alçıpanı deldikten sonra o deliği kapatabilmek. Bu iş için de soba borusu deliği kapatıcısı kullandım. Evin neredeyse hiç fark edilmeyen bir noktasında olduğu için bugüne kadar da kimse fark etmedi.
Bu arada Raspberry Pi Zero W’dan mini USB çıkıyor. O sebeple beyaz renkli dönüştürücü miniUSB’yi USB’ye dönüştürüyor ona da RS485 haberleşme modülü takılıyor. RS485 dönüştürücüden çıkan kablo ise Diris A10’un RS485 portuna bağlanıyor.
Analizörün bağlantısı konusunda detaya girmiyorum. Üzerindeki açıklamalar -eğer birtane satın alırsanız- gayet açıklayıcı.
Bu aşamada analizörün üzerinden RS485 haberleşmesi için gerekli konfigürasyonu yapmanız gerekiyor. Prog tuşuna basılı tutup 100 kodunu girip menüden haberleşme ayarlarını aşağıdaki gibi yapın.
Baudrate = 19.200
Parity = N
Bytesize = 8
Stopbits = 1
Böylelikle cihazın haberleşme portundan nasıl veri beklediğini ayarlamış oluyoruz. Burada atlanmaması gereken bir detay daha var; akım trafosu ayarları. Akım trafolarının oranları vardır 200A/5A gibi. Mesela şu demek; 200A geçtiği zaman ben 5A vereceğim demek; yani 40’da biri. Bu ayarı da satın aldığınız akım trafosuna göre Diris’in ayarlar kısmına girmeniz gerekli. Tüm bu ayarları yaptıktan sonra Raspberry’nin programlanmasına geçebiliriz.
İşte tam bu aşamada fotoğrafları çekmek için çıkardığım ekipmanı geri koyarken SD karta zarar verdim ve yazdığım kodlar gitti. O sebeple ertesi gün oturup tekrar yazdım.
Öncelikle Python3’de kullanacağımız ana kütüphaneyi yüklüyoruz
sudo pip3 install minimalmodbus
Minimalmodbus bizim için gerekecek RS485 Modbus RTU haberleşmesini sağlayacak çok basit bir kütüphane. Şimdi bu kütüphaneyi baz alarak analizör ile haberleşmeyi sağlayan sürücüyü yazalım.
import minimalmodbus
import time
from collections import OrderedDict
from datetime import datetime
from struct import *
class DirisA10( minimalmodbus.Instrument ):
"""Instrument class for Diris A10 Power Meter.
Args:
* portname (str): port name
* slaveaddress (int): slave address in the range 1 to 247
Modbus Addresses of the device are as follows
Read 1:
50514 0xC552 2 Phase to Phase Voltage: U12 V 10⁻² U32
50516 0xC554 2 Phase to Phase Voltage: U23 V 10⁻² U32
50518 0xC556 2 Phase to Phase Voltage: U31 V 10⁻² U32
50520 0xC558 2 Simple voltage : V1 V 10⁻² U32
50522 0xC55A 2 Simple voltage : V2 V 10⁻² U32
50524 0xC55C 2 Simple voltage : V3 V 10⁻² U32
50526 0xC55E 2 Frequency : F Hz 10⁻² U32
50528 0xC560 2 Current : I1 mA U32
50530 0xC562 2 Current : I2 mA U32
50532 0xC564 2 Current : I3 mA U32
50534 0xC566 2 Neutral Current : In mA U32
50536 0xC568 2 ? Active Power +/- : P W 10 S32
50538 0xC56A 2 ? Reactive Power +/- : Q var 10 S32
50540 0xC56C 2 ? Apparent Power : S VA 10 U32
50542 0xC56E 2 ? Power Factor : -: leading et + : lagging : PF - / 1000 S32
Read 2:
50780 0xC65C 2 Partial Positive Active Energy: Ea+ kWh U32
50782 0xC65E 2 Partial Positive Reactive Energy: Er + varh 10³ U32
50784 0xC660 2 Partial Apparent Energy : Es kVAh U32
50786 0xC662 2 Partial Negative Active Energy : Ea- kWh U32
50788 0xC664 2 Partial Negative Reactive Energy : Er - varh 10³ U32
Read 3:
51536 0xC950 1 thd U12 % / 10 U16
51537 0xC951 1 thd U23 % / 10 U16
51538 0xC952 1 thd U31 % / 10 U16
51539 0xC953 1 thd V1 % / 10 U16
51540 0xC954 1 thd V2 % / 10 U16
51541 0xC955 1 thd V3 % / 10 U16
51542 0xC956 1 thd I1 % / 10 U16
51543 0xC957 1 thd I2 % / 10 U16
51544 0xC958 1 thd I3 % / 10 U16
"""
def __init__(self, portname, slaveaddress):
minimalmodbus.BAUDRATE = 19200
minimalmodbus.PARITY = 'N'
minimalmodbus.BYTESIZE = 8
minimalmodbus.STOPBITS = 1
minimalmodbus.TIMEOUT = 1.0
minimalmodbus.Instrument.__init__(self, portname, slaveaddress)
def get_v1(self):
#Usually a test. Gets the V1 value back to user.
return self.read_long(50520, functioncode=3, signed=False, byteorder=0)
def read_all(self):
#Create an ordered dict
values = OrderedDict()
#First read the voltage/current stuff.
registers = self.read_registers(registeraddress=50514, number_of_registers=30, functioncode=3)
time.sleep(1)
# Convert 16 bit registers to longs
values['U12'] = self.convert_registers_to_long(int1=0, int2=1, signed=False, decimals=2, data=registers)
values['U23'] = self.convert_registers_to_long(int1=2, int2=3, signed=False, decimals=2, data=registers)
values['U31'] = self.convert_registers_to_long(int1=4, int2=5, signed=False, decimals=2, data=registers)
values['V1'] = self.convert_registers_to_long(int1=6, int2=7, signed=False, decimals=2, data=registers)
values['V2'] = self.convert_registers_to_long(int1=8, int2=9, signed=False, decimals=2, data=registers)
values['V3'] = self.convert_registers_to_long(int1=10, int2=11, signed=False, decimals=2, data=registers)
values['F'] = self.convert_registers_to_long(int1=12, int2=13, signed=False, decimals=2, data=registers)
values['I1'] = self.convert_registers_to_long(int1=14, int2=15, signed=False, decimals=3, data=registers)
values['I2'] = self.convert_registers_to_long(int1=16, int2=17, signed=False, decimals=3, data=registers)
values['I3'] = self.convert_registers_to_long(int1=18, int2=19, signed=False, decimals=3, data=registers)
values['In'] = self.convert_registers_to_long(int1=20, int2=21, signed=False, decimals=3, data=registers)
values['P'] = self.convert_registers_to_long(int1=22, int2=23, signed=True, decimals=0, data=registers) * 10
values['Q'] = self.convert_registers_to_long(int1=24, int2=25, signed=True, decimals=0, data=registers) * 10
values['S'] = self.convert_registers_to_long(int1=26, int2=27, signed=False, decimals=0, data=registers) * 10
values['Pf'] = self.convert_registers_to_long(int1=28, int2=29, signed=True, decimals=3, data=registers)
#...now the energies :)
registers = self.read_registers(registeraddress=50780, number_of_registers=10, functioncode=3)
time.sleep(1)
#...conversion(Decimal=3 sets them to MW scale.)
values['Active_Energy_Positive'] = self.convert_registers_to_long(int1=0, int2=1, signed=False, decimals=0, data=registers)
values['Reactive_Energy_Positive'] = self.convert_registers_to_long(int1=2, int2=3, signed=False, decimals=0, data=registers)
values['Apparent_Energy'] = self.convert_registers_to_long(int1=4, int2=5, signed=True, decimals=0, data=registers)
values['Active_Energy_Negative'] = self.convert_registers_to_long(int1=6, int2=7, signed=True, decimals=0, data=registers)
values['Reactive_Energy_Negative'] = self.convert_registers_to_long(int1=8, int2=9, signed=True, decimals=0, data=registers)
#...and now the harmonikas!
registers = self.read_registers(registeraddress=51536, number_of_registers=9, functioncode=3)
time.sleep(1)
#...conversion
values['THD_U12'] = registers[0] / 10
values['THD_U23'] = registers[1] / 10
values['THD_U31'] = registers[2] / 10
values['THD_V1'] = registers[3] / 10
values['THD_V2'] = registers[4] / 10
values['THD_V3'] = registers[5] / 10
values['THD_I1'] = registers[6] / 10
values['THD_I2'] = registers[7] / 10
values['THD_I3'] = registers[8] / 10
#...append the time to the values
values['Date'] = self.get_timestamp_for_influxdb()
return values
def convert_registers_to_long(self, int1, int2, signed, decimals=0, data=[]):
decimal = {0: 1,1: 10, 2: 100, 3: 1000}
mypack = pack('>HH',data[int1],data[int2])
if signed:
format = '>l'
else:
format = '>L'
long_data = unpack(format, mypack)
final_data = float(long_data[0]) / decimal[decimals]
return final_data
def get_timestamp_for_influxdb(self):
return datetime.utcnow().strftime("%Y-%m-%dT%H:%M:00Z")
Analizör içerisinde pek çok veri ve modbus adresi var ama bizi ilgilendiren kısımları akım/gerilim/enerji/güç faktörü/harmonikler. Sadece bu kısımları alacak bir sürücü yazdım. Cihazın hangi adreslerde hangi verileri verdiğini öğrenmek isteyenler buraya tıklayabilir. Şimdi kısaca kodu açıklayayım.
Öncelikle minimal modbus bir modbus RTU kütüphanesi. Yani Modbus TCP desteklemiyor; sadece RS485 haberleşmesini destekliyor. Bu kapsamda yukarıda daha önce paylaştığım haberleşme parametrelerini sisteme tanıtıyorum. Minimalmodbus kütüphanesinin tek register okumasını sağlayan read_register, read_long gibi metodları var ama bizde 25-30 farklı veri okuyacağımız için bu verileri ham formatta çekip kendimiz düzenlememiz gerekiyor. Diğer metodla teker teker de okuyabilirsiniz ama yavaş olacaktır.
Tabi veri tiplerinden hiç bahsetmedim. Kısaca veriler 16bit (1 register) ve long 32bit (2 register) olarak ayrılıyor. Daha sonra bu sayıların -‘den +’ya mı gittiğiniz (SIGNED) ya da sadece pozitif değer (UNSIGNED) mi olduğunu bilmeniz gerekiyor. Kodun comment kısmına bakarsanız U32 S32 U16 gibi açıklamalar görürsünüz. Bu açıklama registeri okumadan önce değeri yorumlayabilmeniz için çok önemli. Bu konunun da detayına çok girmeden devam ediyorum.
read_all() diye bir fonsksiyon tanımladım. Bu sırasıyla gidip registerları okuyor ve convert_registers_to_long() metodu ile iki registeri long’a çeviriyor. Bu dönüşüm detaylarına da girmiyorum onlar artık sizin öğrenmeniz gereken temel konular.
Akabinde minimalmodbus kütüphanesi ile analizörden verilerimizi alıp bir dictionary’e atmış oluyoruz ve onu döndürüyoruz. Tabi bunları nereye döndürüyoruz?
import dirisa10
import json
import sys
import time
from datetime import datetime
def main():
trial = 0
while trial <= 10:
try:
power_analyzer = dirisa10.DirisA10('/dev/ttyUSB0', 1)
data = power_analyzer.read_all()
file_name = "/home/pi/solarian/uploads/orcun_ev_"+get_timestamp()+".txt"
with open(file_name, 'w') as dump_file:
json.dump(data, dump_file)
trial += 11
except Exception as e:
trial += 1
print("Error while reading")
time.sleep(1)
print(str(e))
def get_timestamp():
return datetime.today().strftime("%Y%m%d%H%M")
if __name__ == "__main__":
main()
Ana python dosyamızda ise temelde sürücümüzü çağırıp, read_all() fonksiyonundan dönen değerleri influx’a gönderilmek üzere bir klasör altında txt dosyası olarak JSON formatında kaydediyorum.
Bazen analizör sapıtabiliyor ve modbus haberleşmesi sorunlu olabiliyor. Böyle bir durumda tekrar denemeniz gerekebiliyor. Ben 10 kere dene dedim. 10’dan daha fazla hata aldığımı bilmiyorum. Her okuma arasına problem olmasın diye 1 saniye de aralık koydum. Hatalı bir okuma yaklaşık 5 saniye içerisinde tamamlanıyor. En kötü ihtimalle kodun 50 saniyede tamamlanmasını bekliyorum ki genelde 5-8 saniye içerisinde tüm değerleri okumuş oluyor.
Burada oluşan değerleri bir klasör altında toplayıp işlenmek üzere sunucuya gönderiyorum ki orada da bambaşka bir Python+InfluxDB+Grafana altyapısı çalışıyor. Bu nedir diyenlere şuraya ve şuraya bakabilirler.
Sonrasında?
Sonrasında böyle bir arabirimden ne olmuş ne olmamış izleyebilirsiniz.
Raspberry hayatıma girdiğinden beri o kadar çok konuda kullanmaya başladım ki; evdeki NAS sunucusu Raspberry. Şirketin verilerini tutan sunucu raspberry, print server raspberry. Analizörü okuyup verileri kaydeden cihaz raspberry. Şuanda güneş panelleri için bir EL test cihazı yapıyorum ondayine raspberry kullanacağım. Gerçekten muhteşem bir cihaz. Hem açık kütüphaneler hem de fiyatların bu derece düşmesi artık pek çok sözde “endüstriyel” cihazı sorgulatıyor insana.
Galiba en sevdiğim oyuncağım; raspberry…
Corona karantinasından selamlar,
Orçun
Bir yanıt yazın