ESAの彗星探査機「フィラエ」が電力喪失、スリープモードに移行
探査機にとって電力は命です。 フィラエは太陽光パネルに光が当たれば、復活の可能性がありますが、電力源としてバッテリーしか持たないハウスローバーは、それを消費してしまえば、活動できなくなります。 少しでも長くバッテリーを持続させるための制御に利用する計画で、RTCを搭載しました。
RTC-8564NBは、分、時、日、曜日を指定できるアラームと0~255秒(または分)を設定できるタイマーを内蔵しています。 その出力としてINT端子がLowレベルとなるため、Raspberry Piのリセット端子と接続することで、シャットダウン状態のシステムを起動することができます。
I2C接続後、ハードウェアクロックデバイスとして登録します。
/etc/modules-load.d/rasberrypi.conf へ
rtc-pcf8563 を、追加
echo pcf8563 0x51 > /sys/class/i2c-adapter/i2c-1/new_device を、実行する。
これで、hwclockコマンドが使用できるようになります。
ところが、システムモジュールとして登録すると、I2Cコマンドで直接操作ができなくなります。 (pythonでは可能なようですが…)
今回は、アラーム設定を直接操作したいので、システムモジュールには登録しません。 (システムモジュールとして登録されていて、一時的に外す場合は modprobe -r rtc-pcf8563 を実行)
PHPで、RTCを設定するスクリプトを作成しました。
<?php //RTC SET v1.1 2015.1.11 spacelab //ハードウェアクロックとして設定されている場合は //タイムゾーンはUTCが使用されています。 //本スクリプトはJSTとして動作しますので、事前に //時刻設定を行って下さい。 date_default_timezone_set('Asia/Tokyo'); setlocale(LC_ALL,"ja_JP.UTF-8"); $i2cadd = 0x51; //デバイススレーブアドレス $msk3 = 0x07; $msk5 = 0x1f; $msk7 = 0x3f; $msk8 = 0x7f; $bit2 = 0x02; if($argv[1]){ //パラメータチェック $sw = $argv[1]; }else{ $sw = "-d"; } switch ($sw){ case "-d": //RTC時刻の取得 //レジスタの読み込み $r_sec = hexdec(exec("i2cget -y 1 $i2cadd 0x02")); $r_min = hexdec(exec("i2cget -y 1 $i2cadd 0x03")); $r_hou = hexdec(exec("i2cget -y 1 $i2cadd 0x04")); $r_day = hexdec(exec("i2cget -y 1 $i2cadd 0x05")); //$r_wek = exec("i2cget -y 1 0x51 0x06"); $r_mon = hexdec(exec("i2cget -y 1 $i2cadd 0x07")); $r_yea = substr(exec("i2cget -y 1 $i2cadd 0x08"),2) + 2000; //不要ビットをマスク $r_sec = dechex($r_sec & $msk8); $r_min = dechex($r_min & $msk8); $r_hou = dechex($r_hou & $msk7); $r_day = dechex($r_day & $msk7); $r_mon = dechex($r_mon & $msk5); $timestamp = mktime($r_hou,$r_min,$r_sec,$r_mon,$r_day,$r_yea,0); echo strftime("%c\n",$timestamp); break; case "-s": //RTC時刻をシステムタイマーと同期 $r_yea = date('y'); $r_mon = date('m'); $r_day = date('d'); $r_wek = date('w'); $r_hou = date('H'); $r_min = date('i'); $r_sec = date('s'); exec("i2cset -y 1 $i2cadd 0x08 0x".$r_yea); exec("i2cset -y 1 $i2cadd 0x07 0x".$r_mon); exec("i2cset -y 1 $i2cadd 0x06 0x".$r_wek); exec("i2cset -y 1 $i2cadd 0x05 0x".$r_day); exec("i2cset -y 1 $i2cadd 0x04 0x".$r_hou); exec("i2cset -y 1 $i2cadd 0x03 0x".$r_min); exec("i2cset -y 1 $i2cadd 0x02 0x".$r_sec); $timestamp = mktime($r_hou,$r_min,$r_sec,$r_mon,$r_day,$r_yea,0); echo strftime("%c\n",$timestamp); break; case "-w": //RTCをシステム時刻に設定 //レジスタの読み込み $r_sec = hexdec(exec("i2cget -y 1 $i2cadd 0x02")); $r_min = hexdec(exec("i2cget -y 1 $i2cadd 0x03")); $r_hou = hexdec(exec("i2cget -y 1 $i2cadd 0x04")); $r_day = hexdec(exec("i2cget -y 1 $i2cadd 0x05")); //$r_wek = exec("i2cget -y 1 0x51 0x06"); $r_mon = hexdec(exec("i2cget -y 1 $i2cadd 0x07")); $r_yea = substr(exec("i2cget -y 1 $i2cadd 0x08"),2) + 2000; //不要ビットをマスク $r_sec = dechex($r_sec & $msk8); $r_min = dechex($r_min & $msk8); $r_hou = dechex($r_hou & $msk7); $r_day = dechex($r_day & $msk7); $r_mon = dechex($r_mon & $msk5); $timestr = "$r_yea/$r_mon/$r_day $r_hou:$r_min:$r_sec"; exec("date --set='".$timestr."'"); echo exec("date")."\n"; break; case "-as": //アラーム時刻の設定 if($argv[2]){ if($argv[2] =="m"){$a_cas = "0x09";} if($argv[2] =="h"){$a_cas = "0x0a";} if($argv[2] =="d"){$a_cas = "0x0b";} if($argv[2] =="w"){$a_cas = "0x0c";} if($argv[3]){ $a_val = hexdec($argv[3]); }else{ $a_val = 128; } exec("i2cset -y 1 $i2cadd $a_cas $a_val"); exec("i2cset -y 1 $i2cadd 0x01 0x02"); }else{ //アラームセット exec("i2cset -y 1 $i2cadd 0x01 0x02"); echo "アラームセットしました\n"; } case "-a": //アラーム時刻の表示 $a_set = hexdec(exec("i2cget -y 1 $i2cadd 0x01")); $a_min = hexdec(exec("i2cget -y 1 $i2cadd 0x09")); $a_hou = hexdec(exec("i2cget -y 1 $i2cadd 0x0a")); $a_day = hexdec(exec("i2cget -y 1 $i2cadd 0x0b")); $a_wek = hexdec(exec("i2cget -y 1 $i2cadd 0x0c")); if ($a_min >=128){$a_min_on = "無効 ";}else{$a_min_on = "有効 ";} if ($a_hou >=128){$a_hou_on = "無効 ";}else{$a_hou_on = "有効 ";} if ($a_day >=128){$a_day_on = "無効 ";}else{$a_day_on = "有効 ";} if ($a_wek >=128){$a_wek_on = "無効 ";}else{$a_wek_on = "有効 ";} $a_min = dechex($a_min & $msk8); $a_hou = dechex($a_hou & $msk7); $a_day = dechex($a_day & $msk7); $a_wek = dechex($a_wek & $msk3); echo "アラーム設定"; if (dechex($a_set & $bit2) == 2){echo " 有効\n";}else{echo " 無効\n";} echo "Minuite: $a_min_on $a_min\n"; echo "Hour : $a_hou_on $a_hou\n"; echo "Day : $a_day_on $a_day\n"; echo "Week : $a_wek_on $a_wek\n"; break; case "-ar": //アラームリセット exec("i2cset -y 1 $i2cadd 0x01 0x00"); echo "アラームリセットしました\n"; break; case "-ts": //タイマーの設定 if($argv[2]){ exec("i2cset -y 1 $i2cadd 0x0f $argv[2]"); if($argv[3]){ exec("i2cset -y 1 $i2cadd 0x0e 0x83"); }else{ exec("i2cset -y 1 $i2cadd 0x0e 0x82"); } exec("i2cset -y 1 $i2cadd 0x01 0x01"); }else{ echo "パラメータが足りません\n"; } case "-t": //タイマー情報の表示 $t_cnt = hexdec(exec("i2cget -y 1 $i2cadd 0x0e")); $t_dat = hexdec(exec("i2cget -y 1 $i2cadd 0x0f")); echo "タイマー設定"; if ($t_cnt >=128){echo " 有効\n";}else{echo " 無効\n";} if ($t_cnt & 1){$clock = "分";}else{$clock = "秒";} echo "Timer: $t_dat $clock\n"; break; case "-tr": //タイマーリセット exec("i2cset -y 1 $i2cadd 0x01 0x00"); exec("i2cset -y 1 $i2cadd 0x0e 0x00"); echo "タイマーリセットしました\n"; break; case "-h": case "-?": print <<< EOS 使用法: rtcset.php [機能] [設定値]... 機能: -d 現在時刻を表示する -s RTCをシステムクロックと同期する -w RTCの時刻をシステムクロックへ設定する -a アラームの設定状況を表示する -as [m,h,d,w] [値] アラームを有効に設定する [m,h,d,w] 分,時,日,曜日の項目を指定する [値] を省略すると該当項目を無効にする 曜日の値:日=0 月=1 火=2 水=3 木=4 金=5 土=6 例: 10時30分に設定 -as h 10 -as m 30 -ar アラームをリセットし、無効に設定する -t タイマーの設定状況を表示する -ts [値] [秒,分] タイマーを有効に設定する [値] 0-255(秒) 単位: [初期値=秒,1=分] -tr タイマーをリセットし、無効に設定する -h,-? 使い方を表示 -v バージョン情報を表示 EOS; break; case "-v": echo "rtcset.php version 1.1\nCopyright (C) 2015 space laboratory\n"; break; } ?>
php rtcset.php -h で、ヘルプが表示されます。 アラームやタイマーの設定をして、シャットダウンを実行することで、指定の時刻に起動させる事ができるようになりました。
アラーム出力はRaspberry Piにリセットをかけるものですので、メインのソフトウェアで定期的にタイマーを更新するような処理を行うことで、システム異常停止の際に復帰させる、ウォッチドッグタイマーの様な使い方もできると思います。