シェパードのリサ、生きていれば11歳

昨年亡くなったシェパードのリサ。一昨日、生きていれば11歳の誕生日を迎えた。もう少し、一緒に過ごしたかったな…。

元気だった頃の写真を何枚か…。







a






靖国通り~京葉道路~荒川~高島平

自転車のログをアップするのは久しぶり。実は近場を何度か走ってはいるが、面倒でログを取ってなかった。その後、週末私用が重なったり、梅雨入りしてしまったりで走れなかった。

今日は前のログの逆コース。雨がヤバそうだったが、たまたま早起きしたら杉並は晴れ。これは行ける!と思って出発。結局降られずに済んだ。しかも暑い割には朝は気持ちいい。早起きして良かった。

それにしても荒川沿いはやはり走り易い。道幅が広く、信号待ちが無いのが素晴らしい。最近はどこを走ろうか悩んだら荒川沿いかお台場が定番のコースになりつつある。


走行距離は58Km。

Arduino でプロトコルデコーダーを作る

前回からの続きでデコーダー編。これがパネルヒーターとのフロントエンドになり、Linuxが動くPandaboardへUART経由で情報を送る(予定)。パネルヒーターのキー操作のハードとソフトは後日実装。

先ずは結果から。デコーダー側のソフトでは、ジェネレーターが生成したプロトコルを取り込み、デコードした結果の8バイト(64bit)をシリアルで1秒毎に表示させている。Arduino の統合環境内にある Serial Monitor を開くと、
FF
55
AA
0
CC
33
12
FF
と表示される。ジェネレーターが送ったデータと一致。ジェネレーター側の値を変えても、その内容に従って表示された。めでたし、めでたし。

念のため波形も計測してみる。CH1(黄色)はジェネレーター側、CH2(水色の信号)はデコーダー内でデータをキャプチャーした瞬間にトグルさせるデバッグ用の信号である。この両エッジが、ジェネレーター側のデータbitを奇麗に捉えていれば、電気的な問題が無い限りデータは取り込めている。

波形が示す通り、ジェネレーターのデーターbitのど真ん中で捉えている。



以下が開発したデコーダー側のソフト。loop文内では、1秒毎に表示するためのウエイト処理後、割込ハンドラ内でのデータキャプチャー処理のトリガ、1フレームキャプチャーが終わるまでの終了待ち、キャプチャーした内容の表示のみである。

プロトコルジェネレーター同様、正確に時間を刻む必要があるため、デコーダー側も基本はタイマー割込ハンドラ内で全て処理をしている。こちらもステートマシンを組み、フレームの判別、フレームのスタート検出、データ取り込みを行っている。

これらの動作はWORK_CLKusで設定されている通り25μsec毎に正確に実行される。デコーダー側の最小カウンタ値は500μsecなので、500を整数分の1で割切れる値であれば良い。但し、デコーダーはジェネレーター側の信号をサンプリングして同期を取っている。要となる信号の取り込みはフレーム開始の1msの立ち下がりから500μsecなので、ここの検出のずれが取り込むポイントの誤差につながる。あまり大きいと、ジェネレーター側の信号のセットアップとホールドが確保出来なくなり、bit化けが発生する可能性出てくる。なるべく誤差を減らすには、WORK_CLKusの値を小さくして分解能を上げれば良いが、あまり小さ過ぎると割込ハンドラ内で処理できなくなる。

コード内でフレーム間のLOW/10msとスタートフレームのHIGH/1msが若干少なめ設定されている。これは少なめの時間を設定することで、プロトコルジェネレーター(実際はボイラーの実機)からの誤差を吸収するためである。早目にフレームを識別して次のステートに遷移し、確実に信号のエッジを検出できるようにしている。

一番大変だと思っていた処理が終わった。これで一発で実機でも動いてくれるといいんだが…。あとはコントローラーのキー制御。簡単な回路を設計し、Arduino経由でパネルヒーターに接続する。最後はPandaboardとUARTで接続し、Perlでスクリプトを書けば終わり(な、はずだが…)。

#include <TimerOne.h>
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

#define WORK_CLKus 25 // 25 us Working clock in timer handler
#define CAPTURE_RATEus 500 // 500us Caputure sampling rate
#define CAPTURE_RATE_CT ((CAPTURE_RATEus / WORK_CLKus) - 1)
#define L10MS_LENus 9000 // 10ms Length of start frame
#define L10MS_CT (L10MS_LENus / WORK_CLKus)
#define H1MS_LENus 900 // 1ms HIGH period of start frame
#define H1MS_CT (H1MS_LENus / WORK_CLKus)

#define ST_IDLE 0
#define ST_DET_HIGH 1
#define ST_DET_L10MS 2
#define ST_CNT_L10MS 3
#define ST_DET_H1MS 4
#define ST_CNT_H1MS 5
#define ST_DET_CAP 6
#define ST_CAP 7
#define ST_CNT_CAP 8

#define DATASIZE 8
#define FRAMELEN 64

int PinSigin = 12;
int PinSigout = 13;
int PinPWM = 9;

volatile int gBTState = ST_IDLE;
int gBTIndex = 0;
int gBTMask = 0x80;
u8 gSigData[8];

void setup()
{
pinMode(PinSigin, INPUT); // Signal in
pinMode(PinSigout, OUTPUT); // Signal out for debug
pinMode(PinPWM, OUTPUT);
Timer1.initialize(WORK_CLKus); // Set a 50us period
// Setup pwm on pin 9, 50% duty cycle
Timer1.pwm(PinPWM, 512);
// Attaches TimerIRQHandler() as a timer overflow interrupt
Timer1.attachInterrupt(TimerIRQHandler);
Serial.begin(9600);
InitState();
}

void loop()
{
delay(1000);
StartCapture();
WaitForCapture();
PrintCapturedData();
}

void TimerIRQHandler()
{
int value;

static int count = 0;
static int bitCnt = 0;
static int debugPin = LOW;

if (gBTState == ST_CAP) {
debugPin = ~debugPin;
digitalWrite(PinSigout,debugPin);
}

switch (gBTState) {

case ST_DET_HIGH:
if (digitalRead(PinSigin) == HIGH) {
gBTState = ST_DET_L10MS;
}
break;
case ST_DET_L10MS:
if (digitalRead(PinSigin) == LOW) {
gBTState = ST_CNT_L10MS;
count = 0;
}
break;
case ST_CNT_L10MS:
if (digitalRead(PinSigin) == LOW) {
if (++count > L10MS_CT) {
gBTState = ST_DET_H1MS;
}
}
else {
gBTState = ST_DET_HIGH;
}
break;
case ST_DET_H1MS:
if (digitalRead(PinSigin) == HIGH) {
gBTState = ST_CNT_H1MS;
count = 0;
}
break;
case ST_CNT_H1MS:
if (digitalRead(PinSigin) == HIGH) {
if (++count > H1MS_CT) {
gBTState = ST_DET_CAP;
}
}
else {
gBTState = ST_DET_HIGH;
}
break;
case ST_DET_CAP:
if (digitalRead(PinSigin) == LOW) {
InitDataCapture();
gBTState = ST_CNT_CAP;
bitCnt = 0;
count = 0;
}
break;
//
// Start data capture
//
case ST_CNT_CAP:
if (bitCnt > FRAMELEN) {
gBTState = ST_IDLE;
}
else if (++count >= CAPTURE_RATE_CT) {
gBTState = ST_CAP;
count = 0;
++bitCnt;
}
break;

case ST_CAP:
GetData();
gBTState = ST_CNT_CAP;
break;


default:
debugPin = LOW;
digitalWrite(PinSigout,debugPin);
break;
}
}

void InitState()
{
gBTState = ST_IDLE;
}

void StartCapture()
{
gBTState = ST_DET_HIGH;
}

void WaitForCapture()
{
while(gBTState != ST_IDLE);
}

void PrintCapturedData()
{
int i;

for (i = 0; i < DATASIZE; ++i)
Serial.println(gSigData[i],HEX);
Serial.println("-----------");
}


void InitDataCapture()
{
int i;

for (i = 0; i < DATASIZE; ++i)
gSigData[i] = 0x00;

gBTIndex = 0;
gBTMask = 0x80;
}

void GetData()
{
int value;

if (gBTIndex < DATASIZE) {
value = (digitalRead(PinSigin) == HIGH) ? gBTMask : 0;
gSigData[gBTIndex] |= value;
gBTMask >>= 1;

if (gBTMask == 0x00) {
gBTMask = 0x80;
++gBTIndex;
}
}
}

Arduino でプロトコルジェネレーターを作る

先日パネルヒーターをデジタルオシロで解析し、ボイラー(ホスト)とコントローラー(リモコン)との間のプロトコルを解析することで、LED及び7seg-LEDの状態が読めそうなことが判明した。つまり、電源や燃焼状態、現在の水温、設定温度が読み取れることになる。

さて、そのプロトコルをどうやってデコードするかであるが、結局Arduino(アルドゥイーノ)で行うことに決めた。遅い信号なので、選択肢としてはソフト(マイコン)とハード(PLD/FPGA)のどちらでも処理が可能。最近Verilogを書いてないので久々にPLD/FPGAを使ってみたかったのだが、開発環境にMacが使える事や、Cライクな言語で簡単に書けるので学習コストが掛からないという理由からArduinoを選択した。

名前は聞いていたが、こんなに簡単に組込機器が作れるのは反則だろう、というくらい楽であった。開発環境やライブラリが充実しているので、チップのデータシートを読む必要すら基本的にはない。統合環境でCで普通にコードを書いて、コンパイルしてアップロード(ターゲットへ書込み)して終わり。素敵過ぎる。

価格は約3000円。安い。基板に印字される MADE IN ITALY のシルクに反応してしまうのはロードバイク乗りの宿命。パッケージもステッカーが付いていたりでなかなかお洒落。



さて本題に入る。プロトコルのデコーダー(解析ソフト)を書けば良いのだが、流石にμsecオーダーの時間制御が必要なソフトを机上で書き、実機につないで一発で動かすことはかなり難しい。また、パネルヒーターは遠方にあるので、実機につないで長時間のデバッグも出来ない。そのため、先ずはデバッグ環境から作る事にした。

要はプロトコルは判明しているので、パネルヒーターのコントローラーと同じプロトコルを生成するプロトコルジェネレーターを作り、その信号を元にデコーダーをデバッグすることにした。ハードの世界であれば、Verilogでテストベンチを書いてデバッグする当たり前の手法だが、同じ考え方をソフトに適用した。勿論、プロトコルジェネレーターもArduinoで作る。

ということで、今回はプロトコルジェネレーターの解説。次回の記事で予定しているデコーダーとの対向デバッグ環境はこんな雑な感じ。GND弱くない?とか色々あるが、数十KHz以下の信号なのでOKとする。



プロトコルジェネレーターの仕様

 - 基本的には先日解析した実機と同じプロトコルを生成、データコンテンツのみ任意
 - ホスト(HOST)→コントローラー(CTRL)、CTRL→HOSTの2種類のフレームを生成
 - HOST→CTRLはフレーム開始のLOW期間が10ms
 - CTRL→HOSTはフレーム開始のLOW期間が7ms
 - フレーム開始のHIGHは1ms、HIGH→LOW後は500μsec周期で64bitのデータを送信
 - HOST→CTRLは任意の64bitを送信
 - CTRL→HOSTは任意の64bitを送信
 - HOST→CTRL、CTRL→HOSTは交互に現れる

という訳で、開発したプロトコルジェネレータで生成した信号をデジタルオシロで計測した波形は以下の通り。
先ずはHOST→CTRLのフレームが始まる前のLOW期間10ms。完璧。



CTRL→HOSTのフレームが始まる前のLOW期間7ms。



フレームの開始である1msのHIGHの後、0xff 0x55 0xaa ... というデータを500μsec毎に送っている。見る人が見ると、それっぽいデータパターンが読める。



フレームの開始である1msのHIGH、その後の500μsec毎のデータビット。セットアップもホールドもきちんと確保した上での奇麗な波形。我ながら上出来。



ちなみにこれがパネルヒーター実機を解析した時のフレームの先頭。見ての通りほぼ一緒。デコーダーが一発で動くことを祈る。



今回開発したプロトコルジェネレータのソフトは下記。Arduinoが届いてから数時間で書き上げることが出来た。今まで使ったマイコンでは最短かもしれない。

遅いとは言え、μsecオーダーで正確に時間管理をする必要があるため、タイマー割込を使っている。Arduinoの標準ライブラリではμsecやmsec単位のウエイト関数は標準で用意されているが、残念ながら使えない。ライブラリを呼び出しウエイトしている時間は正確なのだが、プログラムコードが動作している時間やデータをアクセスしている時間が考慮されないため、正確に時間を刻むことは出来ない。

Arduinoでタイマー割込は標準でサポートしてないが、探してみたところ、PWMを使った Timer1 というライブラリがあったので使った。

生成された信号は13ピンに出力(送信)される。基本的にはgSigDataHCとgSigDataCHの8バイト(64bit)の内容をフレームのフォーマットに従って交互に出力している。一定の時間(#defineで定義しているWORK_CLKusの25μ周期)で正確に同期実行するため、ステートマシンを組んで処理は全て割込ハンドラ内で実行している。なので、loop文の内容は空である。ソフトの中でカウントする最小値は125μsecなので、WORK_CLKusは125を整数分の1で割り切れる値になる。但し、あまり小さくし過ぎると割込ハンドラ内で処理できなくなる。

基本的にはVerilogでPLD/FPGAを制御させるコードと考え方は一緒。ソフトでもハードでもコードはほぼ一緒というのもおもしろい。
#include 
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

#define WORK_CLKus 25 // 25 us Working clock in timer handler
#define SEND_RATEus 250 // 250us Sending clock rate
#define SEND_RATE_CT ((SEND_RATEus / WORK_CLKus) - 1)
#define L10MS_LENus 10000 // 10ms Length of start frame
#define L10MS_CT ((L10MS_LENus / WORK_CLKus) - 1)
#define L7MS_LENus 7000 // 7ms Length of start frame
#define L7MS_CT ((L7MS_LENus / WORK_CLKus) - 1)
#define H1MS_LENus 1000 // 1ms HIGH period of start frame
#define H1MS_CT ((H1MS_LENus / WORK_CLKus) - 1)
#define L250US_LENus 250 // 250us
#define L250US_CT ((L250US_LENus / WORK_CLKus) - 1)
#define L125US_LENus 125 // 125 us
#define L125US_CT ((L125US_LENus / WORK_CLKus) - 1)

#define ST_LOW_CNT 1
#define ST_STARTH_CNT 2
#define ST_STARTL_CNT 3
#define ST_SIGOUT 4
#define ST_WAIT 5

#define DATASIZE 8
#define FRAMELEN 64
#define FINISH -1

int PinSigout = 13;
int PinDebug = 8;
int PinPWM = 9;

int gBTState = ST_LOW_CNT;
int gCntOfLow;
int gPat = 0;
int gBTMask = 0x80;
int gSigIdx = 0;
u8 *gSigPtr;

// Host to Controller (period time of LOW is 10ms)
u8 gSigDataHC[] = { 0xff, 0x55, 0xaa, 0x00, 0xcc, 0x33, 0x12, 0xff };
// Controller to Host (period time of LOW is 7ms)
u8 gSigDataCH[] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff };

void setup()
{
pinMode(PinSigout, OUTPUT); // Signal of generated prtocol
pinMode(PinDebug, OUTPUT); // Signal for degug
pinMode(PinPWM, OUTPUT);

Timer1.initialize(WORK_CLKus);
// setup pwm on pin 9, 50% duty cycle
Timer1.pwm(PinPWM, 512);
// attaches TimerIRQHandler() as a timer overflow interrupt
Timer1.attachInterrupt(TimerIRQHandler);

InitSendData(gPat);
digitalWrite(PinSigout, LOW);
}

void loop()
{
}

void TimerIRQHandler()
{
static int count = 0;
static int value;

switch (gBTState) {
case ST_LOW_CNT:
if (++count > gCntOfLow) {
digitalWrite(PinSigout, HIGH);
gBTState = ST_STARTH_CNT;
count = 0;
}
break;

case ST_STARTH_CNT:
if (++count > H1MS_CT) {
digitalWrite(PinSigout, LOW);
gBTState = ST_STARTL_CNT;
count = 0;
}
break;

case ST_STARTL_CNT:
if (++count > L125US_CT) {
digitalWrite(PinSigout, LOW);
value = GetBit();
gBTState = ST_SIGOUT;
count = 0;
}
break;

case ST_SIGOUT:
if (value == FINISH) {
if (gPat == 0)
gPat = 1;
else
gPat = 0;
digitalWrite(PinSigout, LOW);
InitSendData(gPat);
gBTState = ST_LOW_CNT;
count = 0;
}
else {
if (++count > SEND_RATE_CT) {
digitalWrite(PinSigout, value);
gBTState = ST_WAIT;
count = 0;
}
}
break;

case ST_WAIT:
if (++count > SEND_RATE_CT) {
digitalWrite(PinSigout, LOW);
value = GetBit();
gBTState = ST_SIGOUT;
count = 0;
}
break;

default:
break;
}
}

void InitSendData(int pat)
{
if (pat == 0) {
gSigPtr = gSigDataHC;
gCntOfLow = L10MS_CT;
}
else {
gSigPtr = gSigDataCH;
gCntOfLow = L7MS_CT;
}
gBTMask = 0x80;
gSigIdx = 0;
}

int GetBit()
{
int value;

if (gSigIdx >= DATASIZE) {
value = FINISH;
}
else {
value = (gSigPtr[gSigIdx] & gBTMask) ? HIGH : LOW;
gBTMask >>= 1;

if (gBTMask == 0x00) {
gBTMask = 0x80;
++gSigIdx;
}
}
return value;
}

blogのデザイン変更

1行の長さを長くしたかったのと、いい加減見飽きたのでblogのデザインを変更。

どうかな…。

Arduino Uno 発注

先日解析したパネルヒーター制御は、Arduino(アルドゥイーノ)で行う方向で検討を始めた。

個人的にはPLD/FPGAを使いたかったのだが、開発ツールにWindowsが必要だったり、書込みのための専用ハードが必要だったりで、環境整備に時間と費用がかかりそうなのがネック。可能ならMac、ダメでも最低Linuxで開発可能なモノは無いかと探したら流行のArduinoに辿り着いた。

Arduino UNOはボードが3000円前後。Macにも統合開発環境があり、プログラムはUSBで接続するだけでオンボード書込みが可能である。ソフト開発は真剣にコーディングしなきゃならないと思いきや、実は非常に楽であった。言語リファレンスを見るとC言語とほぼ同じ。しかもスタートアップや割込ベクタとか、面倒な部分は隠蔽されている。こんなに楽だったとは知らなかった。

取り合えず開発環境をMacにインストールし、コンパイルの確認が出来たので早速ボードを2枚発注した。取り合えず遊んでみよう。

連休9日目:自宅でのんびり

長かった連休も今日で最後。英会話仲間との食事会や八島ヶ原湿原が相当前のように思える。

車で出掛け、銚子丸で遅めのブランチ。東京で回転寿しを色々食べたが、銚子丸はコストパフォーマンスが非常に良い。帰りは吉祥寺のレピキュリアンでケーキを購入。

本当なら自転車に乗りたかったのだが、天気が怪しいので中止。軽く仮眠した後、blogを一気に更新した。

さて、また明日から頑張ろう。

連休8日目:湯沢旅行

6時に起床。朝風呂を浴びて朝食。妻が洋食で私は和食。昨日と同じ。



天候も悪く、回復の見込みも無かったので関越で東京へ。渋滞も無く、あっさり現実の世界に戻って来てしまった。

帰って来てから貯まっていた録画番組の消化。休み中「おひさま」を全然見てなかったので、8本分を一気に見た。おひさまは安曇野つながりで見始めたが、ドラマとして非常に良い出来。今週は泣かされるシーンが多かった。陽子が和成に酔った勢いで涙ながらに告白するシーンは記憶に残る名シーン。言葉を一言一言発する毎に、涙が溢れ、こぼれていく井上真央の演技に完全に引き込まれてしまった。

今も決して楽な時代では無いが、当時から比べれば良い時代なのだろう。先日亡くなった祖母も結婚後、確か数年で祖父が出征し、戦死した。生き証人が身近にいたのだから、本人からもっと当時の話や心境を聞いておけば良かったかな…。

連休7日目:湯沢旅行

6時に起きて朝風呂を浴びて朝食。朝から豪華である。



湯沢温泉ロープウェイでアルプの里まで行ってみた。ガラス越しなので画質がイマイチ。



ここのゴンドラの定員は166名。日本最大級らしい。



アルプの里を軽くトレッキング。ツツジやクリンソウが真っ盛り。







お昼過ぎに下山。昼食は昨日友人がメールで教えてくれたお勧めの2軒目へ。岩原スキー場内にあるイタリアン、ピッツェリア ラ・ロカンダ・デル・ピットーレ岩原。オーダーしたのはアスパラスペシャルとオレンジと生クリームのデザートピザのハーフ&ハーフ。生クリームが溶けて混じらないように、スティック状のパンでパーティーションが切られている。生地も美味いし具もたっぷり。素晴らしいピザであった。



そして夕食もご馳走。妻は魚、私は肉をチョイス。

連休6日目:湯沢旅行

今日からは湯沢方面へ2泊の旅行。上信越自動車道を北上、定番の小布施ハイウェイオアシスで休憩しながら国道117号線を走った。TwitterやFoursquareで情報発信をしていると、新潟に住む友人からメール。新潟の美味しい店を2件紹介してくれた。早速昼食はその店に行ってみることに。

ということで、1件目は八海山酒造直営店の蕎麦屋長森。和の風情溢れる建物が目印。



正直蕎麦の味を語れるほど蕎麦屋に通ってはいなかったが、それでも安曇野で美味いと言われている蕎麦屋には何度か足を運んだ。その中でも上位にランクされるくらいの味であった。天ぷらは価格の割には種類がたくさん。





ということで、3時のチェックインに合わせて宿に到着。宿泊したのは会社が所属している関東ITソフトウェア健康保険組合(ITS)の施設であるTOSLOVE湯沢。保険加入者しか宿泊できない。土日の抽選に当たる確率は極めて低いが、平日は楽勝で取れた。この日に宿泊していたのは3組だけであった。

TOSLOVE館山もそうだったが、部屋が広い。節電中ということもあり、サウナやプールは利用できなかった。風呂は15m×3mくらい。勿論温泉だが、掛け流しではなかった。



夕食は特にオプションを加えない標準的なコース。メインは、私は肉、妻は魚をチョイス。これで充分お腹一杯になる。

連休5日目:自転車トレーニング他

朝一で妻の通院、速攻で帰途に向かう。途中、農道沿いのHAMAフラワーパーク安曇野に安曇野商店街という看板を発見。車を停めて覗いてみると、八百屋?やケーキ屋、パン屋などが集まる商店街が出来ていた。いつの間に…。

帰りに堀金物産センターで山菜おこわ他、食料品を購入。ガソリンを入れて帰宅。買ってきた食料品でブランチを食べた後は自転車トレーニング。ここに置いてあるMTBに乗るのは1年ぶりくらいかもしれない。ガレージ保管なので状態は良いが、タイヤの空気はすっかり抜けていた。取り合えず空気だけ入れて出発。ロードバイクに慣れきっていたので、久しぶりのMTBは新鮮な感じ。フルサスってこんなに乗り心地が良いとは。でも、やはり車重は重い。平均速度も全然違うし。コースは山麓線から堀金付近で安曇野自転車道に入り、豊科南部公園を往復。東京と違って走り易いし空気が良い。

帰宅後、ご近所さんのお誘いで夫婦でお茶に呼ばれた。犬2頭を交えた歓談。やっぱり犬はかわいいなぁ。久しぶりで話が弾んだ。

オーバーヒート、今回は…

前回オーバーヒートをしてしまったボルボS80。サーモスタットの交換でもう大丈夫だろうか。前回と同じ美ヶ原への急勾配の坂に再び挑戦してみた。

登り始めて数分で温度計の針がやはり動き始めた。エンジンの回転は当然高くなるが、道が狭くて速度が時速20~30Km程度しか出ないため、クーラントの冷却効率が悪いのだろう。温度計の針が一目盛りを超えた辺りで車を停止。ボンネットを開けると、クーラントのキャップからクーラントが滲み出て来ていた。アイドリングを続けると1分もしないうちに水温は正常へ。前回とは明らかに違う。やはりサーモスタットは正常に働いているようだ。

数分後、再び車を走らせる。若干水温は上がったものの、何とか急勾配の区間が終わって速度が出る坂へ。その後、水温は正常であった。

それにしても、今時水温計を見ながら走らなければならない車って。早く自分が乗りたい車に買い替えたい…。

連休4日目:姫川源流&親海湿原

遅めのブランチを食べた後、軽く歩こうということで姫川源流&親海湿原へ行って来た。

今日歩いたのは3Km程度。



この季節は植物がかなり生長し、ジャングルのようだった。やはり来るなら雪解け直後の春がいい。福寿草も見に来た事があったが見事だった。水がいつもながら奇麗だ。



そのまま森の中を歩いて隣の親海湿原へ。何も咲いてないだろうと思っていたら幸運にも何種類か花が咲いていた。



この辺りはツツジが今頃なのか。緑とのコントラストが奇麗だ。


連休3日目:八島ヶ原湿原トレッキング

久しぶりに山歩きをしたくなって霧ヶ峰方面へ。八島ヶ原湿原をぐるっと周り、そのまま物見石(物見岩とも言うようだ)へ。何年ぶりだろうか。

歩いたのはこんなコース。湿原はフラットだが、右に伸びる往復は山道。等長配線のようなクネクネ道がその傾斜を物語る。



ツツジには早いし、新緑の季節にしては遅いかと思ったらそうでもなかった。奇麗な緑を堪能出来た。天候・気温も素晴らしいコンディション。安曇野在住時は毎週のようにこんな生活をしていた。今思うと本当に贅沢な暮らしだった。



正面の赤い屋根の建物が駐車場のある場所。



物見石へ到着。



車山まで行く元気はあったが、妻が体力的に自信が無いというので戻る事に。
良い運動であった。

歩行距離約7Km

連続テレビ小説「おひさま」ロケ地

普段は滅多にドラマは見ないのだが、舞台が安曇野という「おひさま」は見ている。ロケ地が近所なので、一応お約束ということでロケ地を見に行った。

良く出て来る水車小屋のロケ地は堀金にある。安曇野在住時は自転車でこの付近を何度か走ったことがあった。確かに人工物が少ない良い場所である。

最近、ネットを張られてしまったようだ。ちょっと残念。



この坂道も見覚えがある。陽子ちゃんと二人の兄が弁当を食べたシーンはここだったような。水車の水路はダミーではなく、天然の小川から水を引くシステムになっていた。

それにしてもこの古びた経年劣化した風合いはどうやって出すのだろう。プロの仕事だ。



ちなみの奥の藁葺き屋根の建物の裏はこんな感じ。大王わさび農場のロケ地にあった建物もこんな感じだった。



ちょっと離れて穂高の中房川近く。陽子ちゃんが子供の頃、荷車に乗っていたシーンはこの場所。

オシロでパネルヒーターのリモコン解析

ご注意

以下の解析や改造は自己責任でお願いします。分解、改造はメーカーの保証が受けられなくなったり、重大な事故が発生するリスクが伴いますので、充分ご注意ください。

先日買ったデジタルストレージオシロを使い、安曇野の自宅に設置してあるセントラルヒーティング(パネルヒーター)のコントローラーを解析してみた。ボイラーはTOYOTOMI FB-07P、コントローラーは同じくTOYOTOMI BR-4678。

結論から言えば、回路構成や、LED & 7seg-LEDへ表示するための内部プロトコルもほぼ解明できたので、4~5本信号を取り出すだけで、電源ON/OFF、温度調整、LED & 7seg-LEDの表示状態の取出しなど、全てが制御できそうである。



ボイラー本体とコントローラーとの間の信号は極性無しの2線式。コントローラーにはバッテリー等は非搭載。



という訳で、早速解析。



解析結果の概要

 ・2線式の信号は電源ラインに変調をかけてコントローラー
  (以降CTRL)、ボイラー(以降HOST)と双方向通信
 ・フレームの空き時間を見て、
  HOST→CTRL、CTRL→HOSTと交互に通信
 ・時計やタイマー機能などは全てHOST側
 ・CTRLはキーの入力とLED、ブザーの出力のみを行う
  ダム端末機能のみ実装
 ・HOST→CTRLは7seg-LEDのエレメント単位で送信
 ・CTRL→HOSTはキーの入力状態をそのまま送信
 ・1フレームは64bit(推測)
  データのサンプリングレート500μsec/bit


コントローラーの解析

HOSTから壁に出ている2線式の信号をダイレクトに見た信号はこんな感じ。DC15Vを基準に、±3Vで通信用の信号が変調されている。



拡大したもの。



回路を追って、変調された信号がデジタル化されたものを見てみる。マイコンに入力される直前。1フレームのサイズは約32ms。



LOWの状態が7ms続くと、CTRL→HOSTへのフレームが送信される。



LOWの状態が10ms続くと、HOST→CTRLへのフレームが送信される。



HOST→CTRLフレームの先頭。800~900μsecのHIGHの後の立ち下がりから500μsecでサンプリングしていったものがHOSTがCTRLに対して送信したデータになる。データはまだ細かく解析してないが、1bitのデータが、7seg-LEDの1エレメントにそのまま対応している。通常、CTRLの時計のコロンが1秒単位で点滅しているが、全く同じ周期で特定のbitがHIGH/LOWしていた。時計の設定モードに入ると、7seg-LEDの全エレメントが点滅するが、それに応じたフレーム内のデータのbitも同じタイミングで更新されているので、LEDの状態をそのまま転送していることでほぼ間違いはないだろう。

100μくらいでサンプリングし、立ち下がりを検出してから500μ毎にデータを取り出せば、セットアップもホールドも充分な状態で信号を取り出せそうである。

CTRL→HOSTフレームも同様で、キーを押している間、同じタイミングで更新されているデータがあった。上述したが、時計自体も実はHOST側が持っている。時計を合わせた後、CTRLを2線式の線から外し、CTRLの電源が切れた状態から再び接続しても、短時間であればエラー表示にならず設定した時間に復帰する。CTRLは入出力に特化しているようである。



キー入力も以前予測していた回路構成とほぼ同じだが、若干違っていた。入力側は1本で、出力側がダイナミックに切り替わる方式であった。

一つのキーを押した時の入力側の信号。2msのパルスが1bit分に対応しており、同じ系列のキーが4個、即ち時系列で4bit並ぶ。出力側は逆流防止のダイオードが入っており、ワイヤードORしたような状態になる。



信号を横取りし、コントローラーに表示されている7seg-LEDを取得したり、擬似的にキー操作を行う方法を考える。最終的にはPandaboardに接続し、ネット経由での制御となる。PICやアルドゥイーノあたりで制御し、PandaboardにUART経由で接続するのも一つだ。PLD/FPGAを使い、PandaboardのGPIOを使って取り出すのも良い。

仕事ならコスト計算や納期うんぬんで決定することになるのだが、趣味なので自由なのだ。さてどうしようか。

連休2日目:庭の手入れ&買物

約2ヶ月近く家を空けると、庭がジャングルに近付く。先ずは刈払機で草刈り。チップソー(金属の歯)でざっくり刈った後に、ナイロンカッターで玄関の際まで奇麗に刈った。その後、玄関がクモの巣や虫の死骸で凄かったので、水洗い。奇麗になった。

草刈り前


草刈り後


どうも切れが悪いと思い、チップソーを見てみると歯が丸い。これじゃ無理だ。混合ガソリンも少なくなってきた。ということで、午後はホームセンターで買い物。途中、車で道を走っていると、見覚えのある車。昨日一緒にランチをした英会話の先生がウインカーを出して私の進行方向に曲がろうとしているのを発見。窓を開けて手を振ると先生も気付いた。教室から自宅へ帰る途中だったようだ。

連休初日:元英会話の友人とランチ

今日は安曇野在住時に習っていた英会話の先生&友人とランチ。先生は相変わらずハイテンション、友人はいつの間にか上達しており、スラスラとメニューを英訳して先生に説明していた。私はPodcastのESLPODを何となく続けているくらいなのだが、改めて劣化ぶりを再認識させられた。やはり話さないとダメだ。こういう場が多いとモチベーションが上がるので非常に良い。

震災があったり、私が忙しかったりで約半年ぶり。色々な話で盛り上がった。店は堀金にあるプルチーノというイタリアン。私は初めて。もう安曇野を離れて3年が過ぎたが、徐々に浦島太郎状態になりつつある。

今回はピザをチョイスし、みんなでシェアして食べた。珍しいレンコンのピザだけ写真を撮り損ねた。何たる不覚。味の方は宅配ピザとは違う、手作り感のある美味しさ。生地は私が好きなふっくらした感じ。具もたっぷり乗っており、サラダ&ドリンク付きで1380円はかなりリーズナブル。












遅いゴールデンウィークスタート

今年のゴールデンウィークは仕事が忙しく、結局ずらすことにした。仕事も一段落し、今日から代休を含めた9連休の休みがスタート。

いつも通り、金曜の夜中に安曇野の自宅へ到着。高速1000円も今回が最後か。土曜~水曜までは安曇野に滞在し、木曜からは福利厚生施設が取れたので新潟に滞在予定。梅雨入りしてしまったのが残念だが、思いっきり楽しもう。
プロフィール

hashiken

Author:hashiken
基本的に自分で作る、直す、メンテする。東京と安曇野の二重生活。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
Photoscope∀
different version
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
検索フォーム