別投稿「ラズベリーパイをカーナビにしてみよう」のラズパイ電源の取り方について。+Bライン(12Vバッテリ)につなぐと車に乗っていない時でもラズパイが起動しっぱなしでバッテリ上がってしまうし、ACCライン(キーを回した時だけ12V)につなぐと車から降りるときに強制オフになってしまうことから、自動的にシャットダウンするように細工してみました。
とりあえず、思いついたいくつかの方法を挙げておきますが、実際に採用したのは下のほうのやり方なので、結論だけを知りたい人はそちらへどうぞ。
案0:問答無用でキーオフして電源オフ
とりあえずお試しで使うことはできるが、次の起動時にfsck(ファイルシステムにエラーがないかどうかのチェックと修復を行うコマンド)が動いて時間がかかったり、下手をするとファイル壊れて起動不能になる可能性もあるからできれば避けたい。
案1:キーオフする前に手動でシャットダウン
最初は良いが、だんだん操作が面倒になって案0に戻ってしまうだろう、というか戻ってしまった。。。
案2:キーオフをラズパイに認識させて自動的にシャットダウンさせる
できればこうしたいが、キーオフした時にはすでに電源喪失している。どうするか?
案2-1:サブ電源で持たせる
数秒間とはいえ5V×1A×仮10sec=50Jものエネルギを持たせるのは意外と大きな電源が必要と解り却下。また充電方法なんかも考慮必要。
案2-2:+Bラインの電源で持たせる
補機バッテリ容量はそれなりに大きい(12V*30Ah=12V*30A*3600sec=1296000J)のでエネルギ量の問題はクリアできる。あとは電源の切替回路をどうするか。ラズパイにシャットダウン命令を送ってからシャットダウン完了するまでのおよそ10秒間を+Bラインから電源供給させたいが、どうやってシャットダウン完了を認識させるか。
案2-2-1:ラズパイからシャットダウン完了信号を出力させる
やりたかったが、シャットダウン完了前にソフトウェア処理で行うポート出力は使えなくなってしまうようで無理そうだった。数秒のディレイは付けられるもののシャットダウン完了には至らず、下記の他案との組み合わせが必要となった。
案2-2-2:ACCラインオフから10秒遅延させるCRタイマ回路を設ける
できそうな案ではあったが、手持ち部品ではモレ電流の影響でとても誤差が大きそうなのと、やはり部品自体が大きそうだったので別案で。
案2-2-3:ACCラインオフから10秒遅延させるサブ回路(ラズベリーパイピコ)を設ける
以前考えたときはどこのお店も在庫ナシで入手困難であきらめていたが、今は入手可能。今回はこれを採用した。
作成した回路
IGがONされたら(12Vになったら)FET経由でラズパイに+B電源が供給され、IGがOFF(0V)になっても10秒間はラズベリーパイピコからFETを駆動してラズパイに+B電源を供給し続けるというもの。
強制リセットSWは、ラズパイがバグって操作不能になってしまった場合に備えて用意しておきます。今回の電源回路とは無関係に、たまに起きるんですよね;;
トランジスタ類は手持ち部品に合わせて2SC1815,2SJ496を使用。
ラズベリーパイピコでIGOFFを検出したらシリアル通信でラズパイに”SD”信号を送信し、ラズパイでは”SD”がきたらshutdown処理を行うようにしている。私の環境ではshutdown処理はおよそ7秒で終わるようでした。
参考ソースコード
ラズベリーパイピコ
100Hz(0.01秒毎)にmainloop関数を呼び出してigin.value()を判定し、IGがOFFになったらシリアル通信で”SD”を送信、10秒後にigout=0として電源オフにする。
# MicroPythonって言語を使用。
# デバッガはThonny
import time
import machine
igin = machine.Pin(10, machine.Pin.IN, machine.Pin.PULL_DOWN) #14pin #PULL_DOWN/PULL_UP
igout = machine.Pin(22, machine.Pin.OUT) #29pin
uart = machine.UART(1, baudrate=38400, tx=machine.Pin(4), rx=machine.Pin(5)) #UART1 Tx= 6pin, Rx= 7pin
#初期値を設定
count_igoff=0
ena_igsw=igin.value()
if(ena_igsw):
print("IGSWは%%有効%%です")
else:
print("IGSWは##無効##です")
#while True:
def mainloop(timer):
if(ena_igsw):
if(igin.value()):
igout.value(1) #通常動作中
count_igoff=0
else:
if(count_igoff<10*100):
uart.write('SD\n')
igout.value(1) #シャットダウン待ち中
if(count_igoff==0):
print("SD")
else:
igout.value(0) #電源オフ
if(count_igoff==10*100):
print("電源オフ")
if(count_igoff<9999):
count_igoff+=1
else:
igout.value(1) #通常動作中
count_igoff=0
tim=machine.Timer()
tim.init(freq=100.0, mode=machine.Timer.PERIODIC, callback=mainloop)
ラズパイ
シリアル通信の設定
/boot/config.txtに下記の行を追加。シリアルポートを/dev/ttyAMA1として認識するのでラズベリーパイピコとの通信用に使用する。
dtoverlay=uart4
“SD”が来たらシャットダウンさせる
ラズパイ電源オンで自動起動されるようにserviceとして登録しておくと良い
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import time
import RPi.GPIO as GPIO
import serial
class Serial_Communication():
comdevice='/dev/ttyAMA1'
# /boot/config.txt に 「dtoverlay=uart4」と書くと、TxD=24pin,RxD=21pinのシリアルポートができる、フロー制御は使わない
def __init__(self):
try:
self.com = serial.Serial(self.comdevice, 38400, timeout=0) #デバイス名、38.4kbpsで接続、タイムアウト待ちはしない
print('ok! '+self.comdevice+'と接続しました')
except:
self.com = None
print('Error! '+self.comdevice+'が見つかりません')
#print(traceback.format_exc())
# 受信バッファ、送信バッファクリア
self.com.reset_input_buffer()
self.com.reset_output_buffer()
self.bufstr=''
# データ受信関数
def receive_serial(self):
rcvdata=[]
tmp_n=0
while True:
#instr=self.com.read()
try:
instr=self.com.read().decode() #.decode()でbyte→str
except:
print('Error! instrのデコード')
#print(str(instr)) #instr代入前にエラーになるので見れない
instr=''
tmp_n+=1
#print('instr='+instr)
if (instr == ''): #strが無くなったらwhile/for文を抜ける
#print('# nuke')
break
elif(instr == ' '): #スペースは無視、バッファにも格納しない
continue
elif(instr != '\r' and instr != '\n'): #改行でなければ受信バッファstrに格納するだけ
self.bufstr += instr
else:
rcvdata.append(self.bufstr) #受信データをリストとして追加、改行コードは無いはず
self.bufstr=''
pass
return rcvdata
# データ送信関数
def send_serial(self, cmd):
print("send data : {0}".format(cmd))
try:
# 改行コードを必ずつけるために、1回削除して、送信時に再度付与する
cmd = cmd.rstrip()
# 改行コードを付与 文字列をバイナリに変換して送信
self.uartport.write((cmd + "\n").encode("utf-8"))
except:
print('Error! '+self.comdevice+'の送信エラー')
#print(traceback.format_exc())
def __del__(self):
pass
if __name__ == '__main__':
#gpio設定変更にroot権限が必要 sudoで実行すること
#teratermだと、「DISPLAY=:0 sudo /home/pi/bin/piremotecontroll.py」みたいにして実行
igsw=InputIGSW()
ser=Serial_Communication()
time.sleep(0.01)
try:
while True:
for instr in ser.receive_serial():
if (instr=='SD'):
key='ShutDown'
print("!!!shutdown!!! by serial通信")
result = subprocess.run('/usr/bin/sudo /usr/bin/killall -TERM rearcamera.py', shell=True)
time.sleep(1.0)
result = subprocess.run('/usr/bin/sudo /sbin/shutdown -h now', shell=True)
else:
key=''
pass
print("hit instr="+instr+" key="+key)
time.sleep(0.01)
pass #while True: ここには来ないはずだけど念のため
except KeyboardInterrupt:
print("終了キーが押されました")
pass #ループを抜ける
コメント