2016年12月19日月曜日

TWE-LITE 2525Aの電池寿命を実測中

 先月から、自宅リビングのドアにTWE-LITE 2525Aを貼り付けて、ドア開閉のパケットを記録しています。季節変化のせいか、平坦だった電源電圧が12月に入ってから急に低くなりました。一カ月余り経過した時点で、電源電圧は2.6Vを切っています。この様子だと、2カ月は持たないかなという感じです。


2016年6月17日金曜日

闇の中で怪しく光るTWE-Lite

 我が家の戸締りチェッカーは、親機がRaspberry Piのある2階であるのに対し、子機は主に1階に設置してあります。子機の全てを親機だけでカバーできないので、親機の真下に当たる1階の部屋に、中継器を置いてあります。使わなくなったPHSの充電器を再利用し、三端子レギュレータで3.3Vの電源を供給しています。
 中継器としては電源つなぐだけでも事は足りるのですが、それだけでは動いている実感が無いので、PWMにLEDをつないで明滅させるようにしています。

 この中継器のソースも、githubで公開しています。

2016年6月8日水曜日

トランシーバーの電池もち見積もり

 TWE-Liteで作ったトランシーバーの電池もちは、およそ3か月以上と見込んでいます。算出の基にした数値は、以下の通りです。
TWE-Lite Warm Sleep(uA)2
TWE-Lite 待機起床時(mA)17
起床間隔(sec)1
起床時間(ms)32
TWE-Lite 受信時(mA)80
TWE-Lite 送信時(mA)20
1通話あたり送信秒数15
1通話あたり受信秒数15
通話回数/日5
単3エネループの容量を2000mAhとすると、130日で電池が空になります。

 電池の消費割合を時間帯別に集計すると、下のグラフのようになります。

 スリープ中の消費は1%をはるかに下回り、1秒毎に32msの受信待ちを行う待機中起床時の消費が大半を占めています。
 起床間隔と起床時間の長さが電池もちを決定づける主なパラメータです。これらは、電池もちと応答性という相反する要求に直結しています。
 受信時はアンプでスピーカーを鳴らすため、消費電流が数十mA~100mA前後と非常に大きくなります。この部分の割合は、使用状況によって大きく変動します。

2016年6月6日月曜日

トランシーバーの回路を再検討中

トランシーバーの回路を見直しています。
イヤホンでなくスピーカーからの出力を可能にしながら、マイクをコンパクトなMEMSマイクにしてオペアンプを省略し、電解コンデンサーを減らしたら、このようになりました。
MEMSマイクとオーディオアンプを使ってオペアンプを省略した回路図
トランシーバーの回路図

ブレッドボード上で組んでみた感じでは、出力は全く問題なし。オペアンプのLPFより低ノイズで聞き取りやすいです。
マイクのゲインが、コンデンサーマイク+オペアンプに比べると、やや物足りない印象です。

2016年6月5日日曜日

TWE-Liteを使った電池ながもちトランシーバー完成

 TWE-Liteを使ったトランシーバーの初号機セットが完成しました。
今回のプラスチックケースは、タカチのLC115H-M2を使いました。
トランシーバーのセット
中はスッカスカです。
適当なケースさえあれば、もっとコンパクトにできそうです。
裏ぶたを開けたところ
TWE-Liteは前面に実装しています

 アプリケーションは、サンプルアプリのApp_Audioを間欠受信に改造したものを書き込んであります。設定が楽になるように、オートペアリングも実装しました。

2016年6月2日木曜日

自転車ビーコン電池交換

 今年もMaker Faire Tokyo 2016に出店応募しましたが落選しました(;_;)。

 2月中旬に入れた電池が一昨日から残量警告状態になったので、今朝交換しました。100円ショップのアルカリ乾電池で3か月半持ちました。
応答性向上のため、スリープ間隔を2秒に短縮して使用しています。3か月以上持ったので、この設定をデフォルトにしようと思います。今使っている送受信機ペアは好調で、呼んでも応答がない空振り状態になった記憶がありません。百発百中で応答があります。

2016年5月26日木曜日

トランシーバー組み立て中

通販に注文した部品がそろってきたので、1,2号機の組み立てを開始しました。
コネクタ部品がないので完成には至っていませんが、ファームウェアを書いて電源とスピーカーを接続したら、ちゃんと音が鳴りました。強いエコーがかかっていて、カラオケのスピーカーで話しているような音が出ます。

2016年5月8日日曜日

トランシーバーの基板を発注

 トランシーバーの基板をElecrowに発注しました。

 こちらは、表面の3Dイメージ。大きな黒丸はスピーカー。
 

 こちらは、裏面の3Dイメージ。ケースはタカチのLC115H-M2です。
基板が届くまでに、部品を用意しなくては。

TWE-Liteのトランシーバを間欠受信に改造

 TWE-LiteのSDKに含まれているオーディオアプリはスリープしない連続動作ですが、これから作ろうとしているトランシーバはインターホン的な利用を想定しているので、少なくとも1-2か月は電池交換せずに使いたいと思っています。
 間欠受信のコツは自転車発見機の制作で理解していたいので、基本のモードが間欠受信となるように改造してみました。

  • 1秒に一度、32msの受信待ちを行う
  • 受信待ちの間にパケットを受信しなかった場合はスリープする
  • パケットを受信した場合はスリープせずに受信した音声データを出力する
  • PTTボタンが押されたらスリープを解除して送信を行う
  • パケット受信またはPTTボタン押下の無い状態が5秒間継続したらスリープする
このような動作で、1日当たり数十秒の通話を行ったとして、単三エネループで2カ月程度はもつ見込みです。

2016年5月1日日曜日

TWE-Liteのトランシーバを試作

TWE-Liteのアナログ通信アプリを、ブレッドボード上で試作してみました。送信側にはマイクアンプを、受信側にはLPF+アンプ+スピーカーを外付けして、普通に聞き取れることが確認できました。


 実際に使いたい用途があるので、これから基板を設計して、プラスチックケースに収納できるようにします。常時通電では電池が持たないので、待ち受け中は間欠受信するようにソフトを改造する予定です。

2016年4月13日水曜日

自転車発見器の無音モード

 閑静な住宅地など、使用する場所によっては、自転車発見器のメロディーが邪魔になる場合が考えられます。そのような場合を想定し、受信機のオプションビットで音を消せるようにしてあります。しかし、この方法では、インタラクティブモードに入らないと切替ができません。
 ところが、もっと単純で利便性の高い方法がありました。その方法とは、休符のみのプリセットメロディーを用意しておくだけです。iPhoneでアラームを無音にする方法と同じです。
 早速、ソースコードを修正して、プリセットメロディーを追加しました。

 因みに、自転車発見器のメロディー停止は、ごく短い休符1個だけのメロディーを再生するという方法で実現しています。これは、ベースにしたApp_Melodyの機能をそのまま引き継いだものです。

2016年2月15日月曜日

TWE-Liteの自転車ビーコンにオートペアリングを実装

 自転車ビーコンにオートペアリングを実装してみた。

 自転車ビーコンは、ペアリングした送信機と受信機の間でのみ通信が成立する。今までのペアリングは、設定モードに入って手でIDを入力して行っていた。入力を注意深く行うのは面倒だし、動作確認するとはいえ、この方法ではいずれ間違いが発生するのは避けられない。

 オートペアリングでは、コールドスタート時に特定のボタンが押されていたらペアリングモードに入り、ペアリング用のパケットで双方が希望するAppIdとランダムに選んだチャンネルを送り合う。暗黙のルールの下にAppIdを決定し、従属的にチャンネルも決定する。ペアリングのパラメータが決まったら、新しいチャンネルで通信を確認し、一定数のパケットが交換できたらEEPROMに設定を保存する。
 オートペアリングで使うAppIdとチャンネルは固定だが、送信出力を最小にする事と、ペアリング用パケットに双方のシリアル番号を含めることによって、確実に1対1でペアリングする。同時に3台以上がペアリングモードに入ったとしても、ペアリングが成功するのは必ず1対1の組み合わせに限られる(または、ペアリングが失敗する)。

 今のところ、ペアリングのパラメータはAppIdとチャンネルだけ。他のパラメータを含めることも可能だが、自転車ビーコンでは、これ以上は必要無い。

ペアリングモードへの入り口は、cbAppColdStart()から呼ばれるvInitHardware()の中。cold startの時にGPIOの状態からモードを設定する箇所に、DI1によるペアリングモード判定を入れている。
if (bPortRead(PORT_INPUT1)) {
sAppData.bPairingMode = TRUE;
}
DI1は押しボタンスイッチなので、M1,M2,M3のようなプルアップ解除は不要。cbAppColdStart()では、オートペアリング用に状態マシンとAppContextを設定。
// 状態遷移マシンの登録
if (sAppData.bPairingMode) {
ToCoNet_Event_Register_State_Machine(vProcessEvCorePairing);
sAppData.prPrsEv = (void*) vProcessEvCorePairing;
sToCoNet_AppContext.bRxOnIdle = TRUE;
sToCoNet_AppContext.u8TxPower = 0; // 最小出力
sToCoNet_AppContext.u32AppId = APP_ID;
sAppData.u8AppIdentifier = u8CCITT8(
(uint8*) &sToCoNet_AppContext.u32AppId, 4);
sToCoNet_AppContext.u8Channel = CHANNEL; // pairing用に固定
sToCoNet_AppContext.u32ChMask = CHMASK;
}
状態マシンのスタートアップでペアリング用のパラメータを初期化。
static void vProcessEvCorePairing(tsEvent *pEv, teEvent eEvent, uint32 u32evarg) {
switch (pEv->eState) {
case E_STATE_IDLE:
if (eEvent == E_EVENT_START_UP) {
sAppData.u16CtRndCt = 0;
sAppData.u32ReqAppId = ToCoNet_u32GetSerial();  // 要求APP ID
sAppData.u8ReqCh = ((ToCoNet_u16GetRand() & 0xF) + 11); // 要求channel
sAppData.u32CandidateAppId = 0;
sAppData.u32AnotherAppId = 0;
sAppData.u8CandidateCh = 0;
sAppData.u16MatchCount = 0;
sAppData.u16PeerMatched = 0;
}
相手をスキャンして、見つかったら合意形成フェーズへ移行。
case E_STATE_APP_PAIR_SCAN:
// ペアリング相手が現れたら提案確認フェーズ
if (0 < sAppData.u16MatchCount) {
vfPrintf(&sSerStream, "!INF PEER EXIST.@%dms"LB, u32TickCount_ms);
ToCoNet_Event_SetState(pEv, E_STATE_APP_PAIR_PROPOSE);
}
合意形成フェーズで1秒待ち、この間に相手からの合意パケットが十分な量受信できていたら、確認フェーズへ移行。
case E_STATE_APP_PAIR_PROPOSE:
if (1000 <= PRSEV_u32TickFrNewState(pEv)) {
// 1秒待ってから判断
sToCoNet_AppContext.bRxOnIdle = FALSE;
ToCoNet_vRfConfig(); // 受信を一旦停止
if (AUTO_PAIR_COUNT_MIN <= sAppData.u16MatchCount
&& AUTO_PAIR_COUNT_MIN <= sAppData.u16PeerMatched) {
ToCoNet_Event_SetState(pEv, E_STATE_APP_PAIR_CONFIRM);
} else {
ToCoNet_Event_SetState(pEv, E_STATE_APP_PAIR_FAILED);
}
}
break;
確認フェーズでは、最初にRF設定を変更。
case E_STATE_APP_PAIR_CONFIRM:
if (eEvent == E_EVENT_NEW_STATE) {
// カウンタを一旦クリア
sAppData.u16MatchCount = 0;
sAppData.u16PeerMatched = 0;
// u32AppIdはcbAppColdStart以外で変更不可なのでu8AppIdentifierだけを変更
sAppData.u8AppIdentifier = u8CCITT8(
(uint8*) &sAppData.u32CandidateAppId, 4);
sToCoNet_AppContext.u8Channel = sAppData.u8CandidateCh;
sToCoNet_AppContext.u32ChMask = (1UL << sAppData.u8CandidateCh);
// 次のパケットのタイミングを仕込む
sAppData.u16CtRndCt = (ToCoNet_u16GetRand() & 0x3);
sToCoNet_AppContext.bRxOnIdle = TRUE;
ToCoNet_vRfConfig(); // 新たなRF設定に切り替える
}
確認フェーズでも、十分な合意パケットが受信できたら、完了フェーズへ移行。
if (1000 <= PRSEV_u32TickFrNewState(pEv)) {
// 1秒待ってから判断
if (AUTO_PAIR_COUNT_MIN <= sAppData.u16MatchCount
&& AUTO_PAIR_COUNT_MIN <= sAppData.u16PeerMatched) {
ToCoNet_Event_SetState(pEv, E_STATE_APP_PAIR_COMPLETE);
} else {
ToCoNet_Event_SetState(pEv, E_STATE_APP_PAIR_FAILED);
}
}
break;
完了フェーズでは、新しいAppIdとチャンネルに更新してEEPROMへ保存。
case E_STATE_APP_PAIR_COMPLETE:
// AppId,Chを書き換えて保存
{
tsFlash sFlash = sAppData.sFlash;
sFlash.sData.u32appid = sAppData.u32CandidateAppId;
sFlash.sData.u8ch = sAppData.u8CandidateCh;
sFlash.sData.u32chmask = (1UL << sAppData.u8CandidateCh);
sFlash.sData.u32appkey = APP_ID;
sFlash.sData.u32ver = VERSION_U32;
bool_t bRet = bFlash_Write(&sFlash, FLASH_SECTOR_NUMBER - 1, 0);
V_PRINT("!INF FlashWrite %s"LB, bRet ? "Success" : "Failed");
vWait(100000);
}
ペアリングモードでやり取りするパケットは、受信コールバックcbToCoNet_vRxEvent()の中でデコード関数に振り分け。
case TOCONET_PACKET_CMD_APP_USER_PAIRING: // auto pairing
if (PRSEV_eGetStateH(sAppData.u8Hnd_vProcessEvCore) == E_STATE_RUNNING) {
vReceivePairingData(psRx);
}
break;
デコード関数の中では、AppIdの大小で優先度を決定。確定したAppIdが送られてきたら、受信数をカウントする。受信数は状態遷移の判断材料として使用する。
// 要求AppId
uint32 u32ReqAppId = G_BE_DWORD();
// 要求ch
uint8 u8ReqCh = G_OCTET();
(void)u8ReqCh;
// 候補AppId
uint32 u32AcceptAppId = G_BE_DWORD();
// 対向AppId
uint32 u32AnotherAppId = G_BE_DWORD();
// 候補ch
uint8 u8AcceptCh = G_OCTET();
(void)u8AcceptCh;
sAppData.u16PeerMatched = G_BE_WORD(); // 相手方pairingマッチカウンタ
if (sAppData.u32CandidateAppId == 0) {
if (u32ReqAppId < sAppData.u32ReqAppId) {
sAppData.u32CandidateAppId = u32ReqAppId;
sAppData.u32AnotherAppId = sAppData.u32ReqAppId;
sAppData.u8CandidateCh = u8ReqCh;
} else if (sAppData.u32ReqAppId < u32ReqAppId) {
sAppData.u32CandidateAppId = sAppData.u32ReqAppId;
sAppData.u32AnotherAppId = u32ReqAppId;
sAppData.u8CandidateCh = sAppData.u8ReqCh;
} else {
// bad case
}
} else if (sAppData.u32CandidateAppId == u32AcceptAppId
&& sAppData.u32AnotherAppId == u32AnotherAppId) {
sAppData.u16MatchCount++;
} else {
// ignore u32AcceptAppId == 0 or bad case(maybe multiple peer).
}
その他、詳細はgithubのソースコードを参照。

2016年2月7日日曜日

自転車発見器(自転車ビーコン)を出品

ヤフオクにて、「自転車発見器(自転車ビーコン)」として出品しました。
開始価格ではやや赤字なのですが、1世代前の試作機なのでサービス価格。

改めて実測してみると、100m届くケースは滅多に無いとわかったので、スペックはMFT2015でチラシに書いたものより控えめに書いています。

さて、売れるかなぁ?