ページ

2022/05/08

ラズパイとラズピコで分電盤センサを作成②(SW最終形)

続いて、SWの最終形はこんなかんじ。

CT2DB.py(ラズパイ側常時起動プログラム)

import time
import datetime
import MySQLdb
from datetime import datetime as dt

import smtplib
import ssl
from email import encoders
from email import message
from email.mime import multipart
from email.mime import text

import serial

#ttyACM0はタイミングによって変わる可能性あり
ser = serial.Serial('/dev/ttyACM0', 115200)

filename = "ログファイル"

#メールを大量に送信しないようにするためのフラグ
sendmail = 0

from subprocess import getoutput

def _update():
    # ADCの電圧を取得する(USBより)
    str_serial = ""
    str_serial = ser.readline()
    while ser.in_waiting:
        str_serial = str_serial + ser.readline()

    #ラズピコからのテキストデータを整形する
    str_serial = str(str_serial).replace('b', '').replace('\\r\\n', '').replace('\'', '')
    nowdatastr = str(str_serial)
    str_list = str(str_serial).split(',')

    #データべースへの書き込み
    connector = MySQLdb.connect(host="localhost", db="log_current", user="root", passwd="パスワード", charset="utf8")
    cursor = connector.cursor()
    sql = u"insert into current values(now(), %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (str_list[0] , str_list[1] , str_list[2] , str_list[3] , str_list[4] , str_list[5] , str_list[6] , str_list[7] , str_list[8]>    #print(sql)
    try:
        cursor.execute(sql)
    except MySQLdb.Error as e:
        print(e)
        nowtime = dt.now()
        nowtimestr = nowtime.strftime('%Y/%m/%d %H:%M:%S')
        fileobj = open(file, 'a', encoding='UTF-8')
        fileobj.write(nowtimestr)
        fileobj.write(e)
        fileobj.close()
    connector.commit()
    cursor.close()

    #画面表示用の整形 0.4A以下の場合はノイズと区別がつかないので( )をつける
    for i in range(10):
        if float(str_list[i]) < 0.4:
            str_list[i] = "(" + str_list[i][:5] + "A)"
        else:
            str_list[i] = str_list[i][:5] + "A"
    str_list[16] = str_list[16][:4]

    #標準出力へ表示
    nowtime = dt.now()
    nowtimestr = nowtime.strftime('%Y/%m/%d %H:%M:%S')
    print(nowtimestr);
    print("CH0:" + str_list[0])
    print("CH1:" + str_list[1])
    print("CH2:" + str_list[2])
    print("Ch3:" + str_list[3])
    print("CH4:" + str_list[4])
    print("CH5:" + str_list[5])
    print("CH6:" + str_list[6])
    print("CH7:" + str_list[7])
    print("CH8:" + str_list[8])
    print("CH9:" + str_list[9])
    print("気温" + str_list[16] + "℃")
    print(" ")

    #ログファイルに現在時刻を書き込む プロセスの稼働監視用
    try:
        file = open(filename, 'w')
        lasttime = file.write(str(time.time()))
    except Exception as e:
        print(e)
    finally:
        file.close()

    #気温センサが70度以上を示した場合、メール送信
    if float(str_list[16]) > 70.0:
        sendAlertMail(1, str_list[16], '')

#メール送信用メソッド
def sendAlertMail(reason, temperature, errorbody):
    global sendmail

    #メール送信は条件を満たしたとき1回のみ行う
    if sendmail == 0:
        smtp_server_host = '送信メールサーバ'
        smtp_server_port = 送信メールサーバのポート番号

        account = 'アカウント名'
        password = 'パスワード'
        from_email = 'メールアドレス'

	msg = multipart.MIMEMultipart()
        msg["Subject"] = "ラズピコ:通報メール"
        msg["To"] = "メールアドレス"
        msg["From"] = "メールアドレス"
        if reason == 0:
            msgstr = "ラズピコが停止しました" + str(temperature) + "℃ " + errorbody
        elif reason == 1:
            msgstr = "高温です " + str(temperature) + "℃ " + errorbody
        elif reason == 999:
            msgstr = "正常です " + str(temperature) + "℃ " + errorbody
        else:
            msgstr = "不明な事象が起こっています " + str(temperature) + "℃ " + errorbody
        msg.attach(text.MIMEText(msgstr, 'plain', 'utf-8'))

        server = smtplib.SMTP(smtp_server_host, smtp_server_port)
        server.ehlo()
        server.starttls()
        server.ehlo()
        server.login(account, password)
        server.send_message(msg)
        server.close()
        sendmail = 1
        print("!!Send Mail!!")

def main():

    while True:
        _update()

if __name__ == '__main__':
    main()

CT_mesurement.py(ラズピコ内のプログラム)

※一部HTML表示が崩れるため、全角文字を使っている部分あり

import machine
import utime

from machine import Pin, SPI

#動作している間はオンボードLEDを点滅させる
led_onboard = machine.Pin(25, machine.Pin.OUT)

#ラズピコ内蔵の温度センサを利用
sensor_temp = machine.ADC(4)
conversation_factor = 3.3 / (65535)

#CSピンを使うことで、1つのSPIバスで2つのADCを制御
cs1 = machine.Pin(5, machine.Pin.OUT)
cs2 = machine.Pin(14, machine.Pin.OUT)
cs1.value(1)
cs2.value(1)

#SPIの初期化
spi = SPI(0, baudrate=300000, polarity=0, phase=0, bits=8, firstbit=SPI.MSB, sck=Pin(2), mosi=Pin(3), miso=Pin(4))

ite1 = 0
ite2 = 0

Vref = 2.447 #TL431の基準電圧(テスタ測定値)
Resistance = [42.2, 41.8, 50.5, 41.9, 41.9, 42.1, 42.0, 41.9, 50.8, 43.2, 43.8, 50.2, 50, 50, 50, 50] #各chの抵抗値(テスタ測定値)
sampling_count = 10000 #RMSをとるサンプリング数
moving_avg = 3 #移動平均区間
CT_ratio = [1866.662, 1792.035, 2238.42, 1870.499, 1865.004, 1849.13, 1781.08, 1849.199, 2231.249, 1842.651, 2000, 2000, 2000, 2000, 2000, 2000] #chごとの計測結果の補正
bias_voltage_bit10 = [445.5, 445.3, 445.8, 445.5, 445.7, 445.0, 445.1, 444.9, 445.5, 446.9, 445.9, 446.0, 0, 0, 0, 0] #各chのバイアス電圧の初期値
prev_bias_voltage_bit10 = []

CHNs = [0, 1, 2, 3, 4, 5, 6, 7]

#10bit ADC MCP3008の場合
read_buf = bytearray(3)
write_buf_mcp3008 = []
for i in CHNs:
    write_buf_mcp3008.append(bytearray([0b00000001,
    	(0b00001000+CHNs[i]) << 4,
        0b00000000]))

for i in range(16):
    prev_bias_voltage_bit10.append([])
    for j in range(moving_avg):
        prev_bias_voltage_bit10[i].append(445)

adc_rawdata_sum = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
avg_voltage = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
avg_current = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

bit10_sum = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

while True: 
    for i in CHNs:
        #ch0~7に対する処理
        cs1.value(0)
        spi.write_readinto(write_buf_mcp3008[i], read_buf)
        cs1.value(1)
        bit10_0 = ((read_buf[1] アンド 0b00000011) << 8) + read_buf[2]
        adc_rawdata_sum[i] += ((((bit10_0 - bias_voltage_bit10[i])/1023)*Vref)**2) #ADCが読み取った値を瞬時電圧に変換し、2乗したものを加算
        bit10_sum[i] += bit10_0
        
        #ch8~15に対する処理
        cs2.value(0)
        spi.write_readinto(write_buf_mcp3008[i], read_buf)
        cs2.value(1)
        bit10_0 = ((read_buf[1] アンド 0b00000011) << 8) + read_buf[2]
        adc_rawdata_sum[i+8] += ((((bit10_0 - bias_voltage_bit10[i+8])/1023)*Vref)**2)
        bit10_sum[i+8] += bit10_0        
    
    ite1 = ite1 + 1
    
    if ite1 > sampling_count - 1:
        for i in CHNs:
            #ch0~7に対する処理
            avg_voltage[i] = (adc_rawdata_sum[i] / sampling_count) ** 0.5 #瞬時電圧の2乗の積算値から平均電圧を計算し、さらに平方根を計算(RMS)
            avg_current[i] = (avg_voltage[i] / Resistance[i]) * CT_ratio[i] #電圧RMSから電流RMSを計算
            adc_rawdata_sum[i] = 0
            #バイアス電圧の移動平均処理
            for j in range(1,moving_avg):
                prev_bias_voltage_bit10[i][j-1] = prev_bias_voltage_bit10[i][j]
            prev_bias_voltage_bit10[i][moving_avg-1] = bit10_sum[i] / sampling_count
            bias_voltage_bit10[i] = sum(prev_bias_voltage_bit10[i]) / len(prev_bias_voltage_bit10[i])
            bit10_sum[i] = 0
            
            #ch8~15に対する処理
            avg_voltage[i+8] = (adc_rawdata_sum[i+8] / sampling_count) ** 0.5
            avg_current[i+8] = (avg_voltage[i+8] / Resistance[i+8]) * CT_ratio[i+8]
            adc_rawdata_sum[i+8] = 0
            for j in range(1,moving_avg):
                prev_bias_voltage_bit10[i+8][j-1] = prev_bias_voltage_bit10[i+8][j]
            prev_bias_voltage_bit10[i+8][moving_avg-1] = bit10_sum[i+8] / sampling_count
            bias_voltage_bit10[i+8] = sum(prev_bias_voltage_bit10[i+8]) / len(prev_bias_voltage_bit10[i+8])
            bit10_sum[i+8] = 0

        print(avg_current) #ラズパイへ各chの電流値をシリアル送信
        ite2 = ite2 + 1
        ite1 = 0
        led_onboard.toggle()
        
        reading_temp = sensor_temp.read_u16() * conversation_factor #周囲温度を計算
        temperature = 27 - (reading_temp - 0.706) / 0.001721

spi.deinit()

ラズパイとラズピコで分電盤センサを作成(14)

回路の実装図作成に取り掛かろうと思ったが、ここで一つ寄り道。 ADCへの入力前に交流電圧に対して直流バイアスをかける手段として、オペアンプを使う方法が出てきた。 アナデジ太郎の回路設計 というか、バイアスをかけたい場合、この方法の方がメジャー・・? と思ったのだが、オペアンプは以...